summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java358
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java85
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java35
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java101
-rw-r--r--core/api/current.txt56
-rw-r--r--core/api/system-current.txt26
-rw-r--r--core/api/test-current.txt24
-rw-r--r--core/java/android/app/ContextImpl.java17
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl2
-rw-r--r--core/java/android/app/Notification.java10
-rw-r--r--core/java/android/app/OWNERS3
-rw-r--r--core/java/android/app/ResourcesManager.java8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java27
-rw-r--r--core/java/android/app/UiAutomationConnection.java5
-rw-r--r--core/java/android/app/pinner-client.aconfig8
-rw-r--r--core/java/android/app/pinner/IPinnerService.aidl12
-rw-r--r--core/java/android/app/pinner/OWNERS10
-rw-r--r--core/java/android/app/pinner/PinnedFileStat.aidl3
-rw-r--r--core/java/android/app/pinner/PinnedFileStat.java133
-rw-r--r--core/java/android/app/pinner/PinnerServiceClient.java77
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl3
-rw-r--r--core/java/android/app/usage/ParcelableUsageEventList.java14
-rw-r--r--core/java/android/app/usage/UsageEvents.java50
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java64
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java123
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl6
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceInternal.java10
-rw-r--r--core/java/android/companion/virtual/camera/VirtualCamera.java23
-rw-r--r--core/java/android/companion/virtual/flags.aconfig7
-rw-r--r--core/java/android/content/AttributionSource.java3
-rw-r--r--core/java/android/content/Context.java23
-rw-r--r--core/java/android/content/Intent.java6
-rw-r--r--core/java/android/content/pm/PackageInfo.java3
-rw-r--r--core/java/android/content/pm/PackageInstaller.java48
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java8
-rw-r--r--core/java/android/content/pm/PackageManager.java11
-rw-r--r--core/java/android/content/pm/flags.aconfig7
-rw-r--r--core/java/android/content/res/flags.aconfig9
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java7
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java3
-rw-r--r--core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java2
-rw-r--r--core/java/android/os/BatteryConsumer.java25
-rw-r--r--core/java/android/os/BatteryStats.java47
-rw-r--r--core/java/android/os/BatteryUsageStats.java35
-rw-r--r--core/java/android/os/ISecurityStateManager.aidl26
-rw-r--r--core/java/android/os/Message.java2
-rw-r--r--core/java/android/os/PowerComponents.java41
-rw-r--r--core/java/android/os/SecurityStateManager.java81
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/os/storage/StorageManager.java17
-rw-r--r--core/java/android/permission/flags.aconfig9
-rw-r--r--core/java/android/provider/Settings.java32
-rw-r--r--core/java/android/security/OWNERS2
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java24
-rw-r--r--core/java/android/service/notification/NotificationStats.java35
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java20
-rw-r--r--core/java/android/view/InputDevice.java31
-rw-r--r--core/java/android/view/SurfaceControl.java120
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java2
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnection.aidl2
-rw-r--r--core/java/android/webkit/IWebViewUpdateService.aidl5
-rw-r--r--core/java/android/widget/RemoteViews.java219
-rw-r--r--core/java/android/window/SystemPerformanceHinter.java4
-rw-r--r--core/java/android/window/TaskFragmentOperation.java14
-rw-r--r--core/java/android/window/TaskFragmentParentInfo.java24
-rw-r--r--core/java/android/window/WindowInfosListenerForTest.java41
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig8
-rw-r--r--core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java13
-rw-r--r--core/java/com/android/internal/display/RefreshRateSettingsUtils.java15
-rw-r--r--core/java/com/android/internal/os/MonotonicClock.java2
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl3
-rw-r--r--core/jni/Android.bp5
-rw-r--r--core/jni/android_view_InputDevice.cpp5
-rw-r--r--core/proto/android/os/batteryusagestats.proto20
-rw-r--r--core/proto/android/server/usagestatsservice_v2.proto10
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/OWNERS4
-rw-r--r--core/res/res/values/config.xml12
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java39
-rw-r--r--core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java15
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java2
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java8
-rw-r--r--core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java3
-rw-r--r--core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java14
-rw-r--r--data/etc/services.core.protolog.json42
-rw-r--r--keystore/java/android/security/KeyStore2.java19
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java3
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java3
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/Android.bp1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt)29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java27
-rw-r--r--location/api/system-current.txt4
-rw-r--r--location/java/android/location/GnssMeasurementRequest.java4
-rw-r--r--location/java/android/location/flags/gnss.aconfig16
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/LoudnessCodecConfigurator.java189
-rw-r--r--media/java/android/media/LoudnessCodecDispatcher.java92
-rw-r--r--media/java/android/media/LoudnessCodecInfo.aidl3
-rw-r--r--media/java/android/media/tv/ad/TvAdManager.java10
-rw-r--r--media/java/android/media/tv/flags/media_tv.aconfig7
-rw-r--r--media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java2
-rw-r--r--packages/CredentialManager/Android.bp2
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt11
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt7
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt369
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt32
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt40
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt (renamed from packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt)2
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt (renamed from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt)4
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt7
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt46
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt36
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt40
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt44
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt50
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt33
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt36
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt13
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt241
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt27
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt86
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt67
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt29
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt130
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt21
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt4
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt4
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt21
-rw-r--r--packages/PackageInstaller/Android.bp3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java5
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java18
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java18
-rw-r--r--packages/SettingsLib/MainSwitchPreference/Android.bp1
-rw-r--r--packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java26
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts4
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java10
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/Android.bp178
-rw-r--r--packages/SystemUI/TEST_MAPPING30
-rw-r--r--packages/SystemUI/aconfig/OWNERS1
-rw-r--r--packages/SystemUI/aconfig/biometrics_framework.aconfig10
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt69
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt)4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt)16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt)132
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt259
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt187
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt)13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt)14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt)0
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt)0
-rw-r--r--packages/SystemUI/res/drawable/notification_material_bg.xml2
-rw-r--r--packages/SystemUI/res/layout/bluetooth_tile_dialog.xml120
-rw-r--r--packages/SystemUI/res/values-night/colors.xml3
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt23
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java (renamed from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt196
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt115
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt96
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt71
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt58
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt5
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt16
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt10
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt211
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt2
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt20
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt (renamed from packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt)18
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt92
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt69
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt59
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt146
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt25
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt22
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt16
-rw-r--r--ravenwood/framework-minus-apex-ravenwood-policies.txt3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java44
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java (renamed from services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java)53
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java115
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java38
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java52
-rw-r--r--services/companion/java/com/android/server/companion/virtual/TEST_MAPPING10
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java39
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java16
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java125
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java7
-rw-r--r--services/core/java/com/android/server/OWNERS6
-rw-r--r--services/core/java/com/android/server/PinnerService.java406
-rw-r--r--services/core/java/com/android/server/SecurityStateManagerService.java109
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java35
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java38
-rw-r--r--services/core/java/com/android/server/Watchdog.java146
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java69
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java5
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java26
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java12
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java16
-rw-r--r--services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java17
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java91
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java24
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java16
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java49
-rw-r--r--services/core/java/com/android/server/audio/LoudnessCodecHelper.java196
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java23
-rw-r--r--services/core/java/com/android/server/display/DisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java175
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java12
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java14
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java6
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java10
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayAdapter.java5
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java16
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java17
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java9
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java99
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java21
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig16
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java42
-rw-r--r--services/core/java/com/android/server/flags/OWNERS1
-rw-r--r--services/core/java/com/android/server/flags/pinner.aconfig8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java52
-rw-r--r--services/core/java/com/android/server/media/AudioAttributesUtils.java125
-rw-r--r--services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java336
-rw-r--r--services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java678
-rw-r--r--services/core/java/com/android/server/media/AudioRoutingUtils.java46
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteController.java29
-rw-r--r--services/core/java/com/android/server/media/DeviceRouteController.java93
-rw-r--r--services/core/java/com/android/server/media/LegacyBluetoothRouteController.java6
-rw-r--r--services/core/java/com/android/server/media/LegacyDeviceRouteController.java26
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java145
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java86
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java19
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java6
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java4
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java9
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java109
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java7
-rw-r--r--services/core/java/com/android/server/power/stats/AggregatedPowerStats.java8
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java16
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java40
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java315
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java169
-rw-r--r--services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java18
-rw-r--r--services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java208
-rw-r--r--services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java54
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsAggregator.java29
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsCollector.java199
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsExporter.java227
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsScheduler.java30
-rw-r--r--services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java241
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java25
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java8
-rw-r--r--services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java194
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java5
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java43
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java18
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java94
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java4
-rw-r--r--services/core/java/com/android/server/wm/DimmerAnimationHelper.java334
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java4
-rw-r--r--services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java1
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/SmoothDimmer.java423
-rw-r--r--services/core/java/com/android/server/wm/Task.java146
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java24
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java19
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd63
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt17
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java39
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/permission/OWNERS4
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt8
-rw-r--r--services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java293
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java82
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java95
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java23
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java14
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java7
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java29
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java19
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java7
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java126
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java118
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java17
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java179
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt11
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java5
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java11
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java100
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java32
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java89
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java12
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java37
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java6
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java63
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java15
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java25
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java316
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java72
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/PinnerServiceTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/SecurityStateTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/WatchdogTest.java152
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java)433
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java153
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java166
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java247
-rw-r--r--services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java235
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java18
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java193
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java40
-rw-r--r--services/tests/wmtests/AndroidManifest.xml9
-rw-r--r--services/tests/wmtests/AndroidTest.xml4
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java138
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java39
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java181
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java68
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java39
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java80
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java99
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java4
-rw-r--r--telephony/java/android/telephony/CellularIdentifierDisclosure.java161
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java10
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java20
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java2
-rwxr-xr-xtools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh12
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt40
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt219
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt4
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt2
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt20
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt18
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt20
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt18
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt24
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java9
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java2
637 files changed, 14832 insertions, 6770 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c80f2eace19a..b3e8ea8fabf7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -55,6 +55,7 @@ aconfig_srcjars = [
":android.app.flags-aconfig-java{.generated_srcjars}",
":android.credentials.flags-aconfig-java{.generated_srcjars}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.server.flags.pinner-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
":android.media.tv.flags-aconfig-java{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
@@ -584,6 +585,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Pinner Service
+aconfig_declarations {
+ name: "com.android.server.flags.pinner-aconfig",
+ package: "com.android.server.flags",
+ srcs: ["services/core/java/com/android/server/flags/pinner.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.server.flags.pinner-aconfig-java",
+ aconfig_declarations: "com.android.server.flags.pinner-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Voice
aconfig_declarations {
name: "android.service.voice.flags-aconfig",
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 158d914575c6..e6c94d896e50 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -32,6 +32,7 @@ import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -41,6 +42,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -81,6 +83,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.WearModeManagerInternal;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
@@ -109,6 +112,7 @@ import com.android.server.deviceidle.DeviceIdleConstraintTracker;
import com.android.server.deviceidle.IDeviceIdleConstraint;
import com.android.server.deviceidle.TvConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
@@ -1020,7 +1024,8 @@ public class DeviceIdleController extends SystemService
* global Settings. Any access to this class or its fields should be done while
* holding the DeviceIdleController lock.
*/
- public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
+ public final class Constants extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
@@ -1396,6 +1401,7 @@ public class DeviceIdleController extends SystemService
/**
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
+ *
* @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
*/
public long NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
@@ -1413,9 +1419,14 @@ public class DeviceIdleController extends SystemService
*/
public boolean USE_MODE_MANAGER = mDefaultUseModeManager;
+ private final ContentResolver mResolver;
private final boolean mSmallBatteryDevice;
+ private final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
- public Constants() {
+ public Constants(Handler handler, ContentResolver resolver) {
+ super(handler);
+ mResolver = resolver;
initDefault();
mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
if (mSmallBatteryDevice) {
@@ -1424,8 +1435,14 @@ public class DeviceIdleController extends SystemService
}
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DEVICE_IDLE,
AppSchedulingModuleThread.getExecutor(), this);
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
+ false, this);
// Load all the constants.
- onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateSettingsConstantLocked();
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateConstantsLocked();
}
private void initDefault() {
@@ -1574,188 +1591,166 @@ public class DeviceIdleController extends SystemService
return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
}
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (DeviceIdleController.this) {
+ updateSettingsConstantLocked();
+ updateConstantsLocked();
+ }
+ }
+
+ private void updateSettingsConstantLocked() {
+ try {
+ mUserSettingDeviceConfigMediator.setSettingsString(
+ Settings.Global.getString(mResolver,
+ Settings.Global.DEVICE_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on with previous values.
+ Slog.e(TAG, "Bad device idle settings", e);
+ }
+ }
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
synchronized (DeviceIdleController.this) {
- for (String name : properties.getKeyset()) {
- if (name == null) {
- continue;
- }
- switch (name) {
- case KEY_FLEX_TIME_SHORT:
- FLEX_TIME_SHORT = properties.getLong(
- KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
- break;
- case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
- LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- mDefaultLightIdleAfterInactiveTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT:
- LIGHT_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
- LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
- mDefaultLightIdleTimeoutInitialFlex);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX:
- LIGHT_IDLE_TIMEOUT_MAX_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
- mDefaultLightIdleTimeoutMaxFlex);
- break;
- case KEY_LIGHT_IDLE_FACTOR:
- LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
- break;
- case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
- LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
- KEY_LIGHT_IDLE_INCREASE_LINEARLY,
- mDefaultLightIdleIncreaseLinearly);
- break;
- case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleFlexLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_MAX_IDLE_TIMEOUT:
- LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
- break;
- case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
- LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
- KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
- mDefaultLightIdleMaintenanceMinBudget);
- break;
- case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
- LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
- KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
- mDefaultLightIdleMaintenanceMaxBudget);
- break;
- case KEY_MIN_LIGHT_MAINTENANCE_TIME:
- MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_LIGHT_MAINTENANCE_TIME,
- mDefaultMinLightMaintenanceTime);
- break;
- case KEY_MIN_DEEP_MAINTENANCE_TIME:
- MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_DEEP_MAINTENANCE_TIME,
- mDefaultMinDeepMaintenanceTime);
- break;
- case KEY_INACTIVE_TIMEOUT:
- final long defaultInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
- : mDefaultInactiveTimeout;
- INACTIVE_TIMEOUT = properties.getLong(
- KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
- break;
- case KEY_SENSING_TIMEOUT:
- SENSING_TIMEOUT = properties.getLong(
- KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
- break;
- case KEY_LOCATING_TIMEOUT:
- LOCATING_TIMEOUT = properties.getLong(
- KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
- break;
- case KEY_LOCATION_ACCURACY:
- LOCATION_ACCURACY = properties.getFloat(
- KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT:
- MOTION_INACTIVE_TIMEOUT = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
- MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
- mDefaultMotionInactiveTimeoutFlex);
- break;
- case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
- final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
- : mDefaultIdleAfterInactiveTimeout;
- IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
- KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
- defaultIdleAfterInactiveTimeout);
- break;
- case KEY_IDLE_PENDING_TIMEOUT:
- IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
- break;
- case KEY_MAX_IDLE_PENDING_TIMEOUT:
- MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
- break;
- case KEY_IDLE_PENDING_FACTOR:
- IDLE_PENDING_FACTOR = properties.getFloat(
- KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
- break;
- case KEY_QUICK_DOZE_DELAY_TIMEOUT:
- QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
- KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
- break;
- case KEY_IDLE_TIMEOUT:
- IDLE_TIMEOUT = properties.getLong(
- KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
- break;
- case KEY_MAX_IDLE_TIMEOUT:
- MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
- break;
- case KEY_IDLE_FACTOR:
- IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, mDefaultIdleFactor);
- break;
- case KEY_MIN_TIME_TO_ALARM:
- MIN_TIME_TO_ALARM = properties.getLong(
- KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
- break;
- case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
- MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultMaxTempAppAllowlistDurationMs);
- break;
- case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
- MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultMmsTempAppAllowlistDurationMs);
- break;
- case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
- SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultSmsTempAppAllowlistDurationMs);
- break;
- case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
- NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
- mDefaultNotificationAllowlistDurationMs);
- break;
- case KEY_WAIT_FOR_UNLOCK:
- WAIT_FOR_UNLOCK = properties.getBoolean(
- KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
- break;
- case KEY_USE_WINDOW_ALARMS:
- USE_WINDOW_ALARMS = properties.getBoolean(
- KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
- break;
- case KEY_USE_MODE_MANAGER:
- USE_MODE_MANAGER = properties.getBoolean(
- KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
- break;
- default:
- Slog.e(TAG, "Unknown configuration key: " + name);
- break;
- }
- }
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
+ updateConstantsLocked();
}
}
+ private void updateConstantsLocked() {
+ if (mSmallBatteryDevice) return;
+ FLEX_TIME_SHORT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
+
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ mDefaultLightIdleAfterInactiveTimeout);
+
+ LIGHT_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
+
+ LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+ mDefaultLightIdleTimeoutInitialFlex);
+
+ LIGHT_IDLE_TIMEOUT_MAX_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+ mDefaultLightIdleTimeoutMaxFlex);
+
+ LIGHT_IDLE_FACTOR = Math.max(1, mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
+
+ LIGHT_IDLE_INCREASE_LINEARLY = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+ mDefaultLightIdleIncreaseLinearly);
+
+ LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleLinearIncreaseFactorMs);
+
+ LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleFlexLinearIncreaseFactorMs);
+
+ LIGHT_MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
+
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
+ mDefaultLightIdleMaintenanceMinBudget);
+
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+ mDefaultLightIdleMaintenanceMaxBudget);
+
+ MIN_LIGHT_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_LIGHT_MAINTENANCE_TIME,
+ mDefaultMinLightMaintenanceTime);
+
+ MIN_DEEP_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_DEEP_MAINTENANCE_TIME,
+ mDefaultMinDeepMaintenanceTime);
+
+ final long defaultInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultInactiveTimeout;
+ INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
+
+ SENSING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
+
+ LOCATING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
+
+ LOCATION_ACCURACY = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
+
+ MOTION_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
+
+ MOTION_INACTIVE_TIMEOUT_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+ mDefaultMotionInactiveTimeoutFlex);
+
+ final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultIdleAfterInactiveTimeout;
+ IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+ defaultIdleAfterInactiveTimeout);
+
+ IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
+
+ MAX_IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
+
+ IDLE_PENDING_FACTOR = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
+
+ QUICK_DOZE_DELAY_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
+
+ IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
+
+ MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
+
+ IDLE_FACTOR = mUserSettingDeviceConfigMediator.getFloat(KEY_IDLE_FACTOR,
+ mDefaultIdleFactor);
+
+ MIN_TIME_TO_ALARM = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
+
+ MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMaxTempAppAllowlistDurationMs);
+
+ MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMmsTempAppAllowlistDurationMs);
+
+ SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultSmsTempAppAllowlistDurationMs);
+
+ NOTIFICATION_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
+ mDefaultNotificationAllowlistDurationMs);
+
+ WAIT_FOR_UNLOCK = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
+
+ USE_WINDOW_ALARMS = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
+
+ USE_MODE_MANAGER = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
+ }
+
void dump(PrintWriter pw) {
pw.println(" Settings:");
@@ -2490,9 +2485,10 @@ public class DeviceIdleController extends SystemService
return mConnectivityManager;
}
- Constants getConstants(DeviceIdleController controller) {
+ Constants getConstants(DeviceIdleController controller, Handler handler,
+ ContentResolver resolver) {
if (mConstants == null) {
- mConstants = controller.new Constants();
+ mConstants = controller.new Constants(handler, resolver);
}
return mConstants;
}
@@ -2650,7 +2646,7 @@ public class DeviceIdleController extends SystemService
}
}
- mConstants = mInjector.getConstants(this);
+ mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
readConfigFileLocked();
updateWhitelistAppIdsLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d2150b80761f..8381d1a322e0 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -109,7 +109,6 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -154,7 +153,6 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -241,35 +239,36 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
- final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES);
@@ -279,49 +278,49 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
// run out of credits, and not when the system has run out of stock.
mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES),
exactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_EXACT,
new Action(ACTION_ALARM_WAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
final long inexactAllowWhileIdleWakeupBasePrice =
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES),
inexactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
new Action(ACTION_ALARM_WAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES),
exactAllowWhileIdleNonWakeupBasePrice,
@@ -329,18 +328,18 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
new Action(ACTION_ALARM_NONWAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
- final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
@@ -350,72 +349,72 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES)));
mActions.put(ACTION_ALARM_CLOCK,
new Action(ACTION_ALARM_CLOCK,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index a4043dd8ba78..61096b9fa179 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -33,9 +33,9 @@ import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -197,10 +197,17 @@ public abstract class EconomicPolicy {
}
protected final InternalResourceService mIrs;
+ protected final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator;
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
mIrs = irs;
+ // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
+ // config can cause issues since the scales may be different, so use one or the other.
+ // If user settings exist, then just stick with the Settings constants, even if there
+ // are invalid values.
+ mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
for (int mId : getCostModifiers()) {
initModifier(mId, irs);
}
@@ -464,28 +471,14 @@ public abstract class EconomicPolicy {
return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) {
- return getConstantAsCake(parser, properties, key, defaultValCake, 0);
+ protected long getConstantAsCake(String key, long defaultValCake) {
+ return getConstantAsCake(key, defaultValCake, 0);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake,
- long minValCake) {
- // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
- // config can cause issues since the scales may be different, so use one or the other.
- if (parser.size() > 0) {
- // User settings take precedence. Just stick with the Settings constants, even if there
- // are invalid values. It's not worth the time to evaluate all the key/value pairs to
- // make sure there are valid ones before deciding.
- return Math.max(minValCake,
- parseCreditValue(parser.getString(key, null), defaultValCake));
- }
- if (properties != null) {
- return Math.max(minValCake,
- parseCreditValue(properties.getString(key, null), defaultValCake));
- }
- return Math.max(minValCake, defaultValCake);
+ protected long getConstantAsCake(String key, long defaultValCake, long minValCake) {
+ return Math.max(minValCake,
+ parseCreditValue(
+ mUserSettingDeviceConfigMediator.getString(key, null), defaultValCake));
}
@VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 91a291fe20db..69e57365b6e0 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -127,7 +127,6 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -168,7 +167,6 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -274,176 +272,177 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_CTP,
DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_CTP,
DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_CTP,
DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES)));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_APP_INSTALL,
new Reward(REWARD_APP_INSTALL,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_INSTANT,
DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_ONGOING,
DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_MAX,
DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9d13d8ad7905..805ecd43e3c7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9342,6 +9342,7 @@ package android.app.usage {
method public String getClassName();
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
+ method @FlaggedApi("android.app.usage.user_interaction_type_api") @NonNull public android.os.PersistableBundle getExtras();
method public String getPackageName();
method public String getShortcutId();
method public long getTimeStamp();
@@ -9407,6 +9408,8 @@ package android.app.usage {
method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_ACTION = "android.app.usage.extra.EVENT_ACTION";
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_CATEGORY = "android.app.usage.extra.EVENT_CATEGORY";
field public static final int INTERVAL_BEST = 4; // 0x4
field public static final int INTERVAL_DAILY = 0; // 0x0
field public static final int INTERVAL_MONTHLY = 2; // 0x2
@@ -9851,7 +9854,7 @@ package android.content {
method @NonNull public android.content.AttributionSource build();
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
- method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
method @NonNull public android.content.AttributionSource.Builder setPid(int);
@@ -10598,6 +10601,7 @@ package android.content {
field public static final String RESTRICTIONS_SERVICE = "restrictions";
field public static final String ROLE_SERVICE = "role";
field public static final String SEARCH_SERVICE = "search";
+ field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
field public static final String SENSOR_SERVICE = "sensor";
field public static final String SHORTCUT_SERVICE = "shortcut";
field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -10610,6 +10614,7 @@ package android.content {
field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
+ field @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public static final String TV_AD_SERVICE = "tv_ad";
field public static final String TV_INPUT_SERVICE = "tv_input";
field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
field public static final String UI_MODE_SERVICE = "uimode";
@@ -11098,6 +11103,7 @@ package android.content {
field public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
field @Deprecated public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
field @Deprecated public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+ field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field @Deprecated public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
field public static final String ACTION_USER_BACKGROUND = "android.intent.action.USER_BACKGROUND";
field public static final String ACTION_USER_FOREGROUND = "android.intent.action.USER_FOREGROUND";
@@ -12363,6 +12369,7 @@ package android.content.pm {
public class PackageInfo implements android.os.Parcelable {
ctor public PackageInfo();
method public int describeContents();
+ method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
method public long getLongVersionCode();
method public void setLongVersionCode(long);
method public void writeToParcel(android.os.Parcel, int);
@@ -12419,6 +12426,9 @@ package android.content.pm {
method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull String, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, int, @NonNull android.content.IntentSender);
@@ -12440,6 +12450,10 @@ package android.content.pm {
field public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
field public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
field public static final int PACKAGE_SOURCE_DOWNLOADED_FILE = 4; // 0x4
field public static final int PACKAGE_SOURCE_LOCAL_FILE = 3; // 0x3
field public static final int PACKAGE_SOURCE_OTHER = 1; // 0x1
@@ -12455,6 +12469,13 @@ package android.content.pm {
field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
field public static final int STATUS_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
+ field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
}
public static final class PackageInstaller.InstallConstraints implements android.os.Parcelable {
@@ -12618,6 +12639,7 @@ package android.content.pm {
method @RequiresPermission(android.Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) public void setRequestUpdateOwnership(boolean);
method public void setRequireUserAction(int);
method public void setSize(long);
+ method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
method public void setWhitelistedRestrictedPermissions(@Nullable java.util.Set<java.lang.String>);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionParams> CREATOR;
@@ -12647,6 +12669,7 @@ package android.content.pm {
method public void writeToParcel(android.os.Parcel, int);
field public int banner;
field public int icon;
+ field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
field public int labelRes;
field public int logo;
field public android.os.Bundle metaData;
@@ -12769,6 +12792,7 @@ package android.content.pm {
method public boolean hasSigningCertificate(int, @NonNull byte[], int);
method public abstract boolean hasSystemFeature(@NonNull String);
method public abstract boolean hasSystemFeature(@NonNull String, int);
+ method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean isAutoRevokeWhitelisted(@NonNull String);
method public boolean isAutoRevokeWhitelisted();
method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable);
@@ -13010,6 +13034,7 @@ package android.content.pm {
field public static final int INSTALL_SCENARIO_FAST = 1; // 0x1
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_APEX = 1073741824; // 0x40000000
+ field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -22031,6 +22056,19 @@ package android.media {
method public void onJetUserIdUpdate(android.media.JetPlayer, int, int);
}
+ @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void removeMediaCodec(@NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public void setAudioTrack(@Nullable android.media.AudioTrack);
+ }
+
+ @FlaggedApi("android.media.audio.loudness_configurator_api") public static interface LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener {
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public default android.os.Bundle onLoudnessCodecUpdate(@NonNull android.media.MediaCodec, @NonNull android.os.Bundle);
+ }
+
public class MediaActionSound {
ctor public MediaActionSound();
method public void load(int);
@@ -27299,6 +27337,13 @@ package android.media.tv {
}
+package android.media.tv.ad {
+
+ @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdManager {
+ }
+
+}
+
package android.media.tv.interactive {
public final class AppLinkInfo implements android.os.Parcelable {
@@ -32837,7 +32882,7 @@ package android.os {
method public static android.os.Message obtain(android.os.Handler, int, Object);
method public static android.os.Message obtain(android.os.Handler, int, int, int);
method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
- method public android.os.Bundle peekData();
+ method @Nullable public android.os.Bundle peekData();
method public void recycle();
method public void sendToTarget();
method public void setAsynchronous(boolean);
@@ -33376,6 +33421,13 @@ package android.os {
field @NonNull public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ @FlaggedApi("android.os.security_state_service") public class SecurityStateManager {
+ method @FlaggedApi("android.os.security_state_service") @NonNull public android.os.Bundle getGlobalSecurityState();
+ field public static final String KEY_KERNEL_VERSION = "kernel_version";
+ field public static final String KEY_SYSTEM_SPL = "system_spl";
+ field public static final String KEY_VENDOR_SPL = "vendor_spl";
+ }
+
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
method public void close();
method @NonNull public static android.os.SharedMemory create(@Nullable String, int) throws android.system.ErrnoException;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ce5752fdbd8b..0497c6087ccd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -258,6 +258,7 @@ package android {
field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
+ field @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis") public static final String PREPARE_FACTORY_RESET = "android.permission.PREPARE_FACTORY_RESET";
field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
@@ -3613,7 +3614,6 @@ package android.content {
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
- field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -3861,16 +3861,9 @@ package android.content.pm {
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
- public class PackageInfo implements android.os.Parcelable {
- method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
- }
-
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3881,23 +3874,12 @@ package android.content.pm {
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
- field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64
- field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0
}
public static class PackageInstaller.InstallInfo {
@@ -3947,14 +3929,12 @@ package android.content.pm {
method public void setRequestDowngrade(boolean);
method @FlaggedApi("android.content.pm.rollback_lifetime") @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void setRollbackLifetimeMillis(long);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
- method @FlaggedApi("android.content.pm.archiving") public void setUnarchiveId(int);
}
public class PackageItemInfo {
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
- field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
}
public abstract class PackageManager {
@@ -3986,7 +3966,6 @@ package android.content.pm {
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
@@ -4104,7 +4083,6 @@ package android.content.pm {
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
@@ -12213,6 +12191,7 @@ package android.service.notification {
method public boolean hasExpanded();
method public boolean hasInteracted();
method public boolean hasSeen();
+ method @FlaggedApi("android.app.lifetime_extension_refactor") public boolean hasSmartReplied();
method public boolean hasSnoozed();
method public boolean hasViewedSettings();
method public void setDirectReplied();
@@ -12220,6 +12199,7 @@ package android.service.notification {
method public void setDismissalSurface(int);
method public void setExpanded();
method public void setSeen();
+ method @FlaggedApi("android.app.lifetime_extension_refactor") public void setSmartReplied();
method public void setSnoozed();
method public void setViewedSettings();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f3bad3aa82e0..f4c8429619dd 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -780,6 +780,25 @@ package android.app.job {
}
+package android.app.pinner {
+
+ @FlaggedApi("android.app.pinner_service_client_api") public final class PinnedFileStat implements android.os.Parcelable {
+ ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnedFileStat(@NonNull String, long, @NonNull String);
+ method @FlaggedApi("android.app.pinner_service_client_api") public int describeContents();
+ method @FlaggedApi("android.app.pinner_service_client_api") public long getBytesPinned();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public String getFilename();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public String getGroupName();
+ method @FlaggedApi("android.app.pinner_service_client_api") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.app.pinner_service_client_api") @NonNull public static final android.os.Parcelable.Creator<android.app.pinner.PinnedFileStat> CREATOR;
+ }
+
+ @FlaggedApi("android.app.pinner_service_client_api") public class PinnerServiceClient {
+ ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnerServiceClient();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public java.util.List<android.app.pinner.PinnedFileStat> getPinnerStats();
+ }
+
+}
+
package android.app.prediction {
public final class AppPredictor {
@@ -4201,8 +4220,13 @@ package android.window {
public static class WindowInfosListenerForTest.WindowInfo {
field @NonNull public final android.graphics.Rect bounds;
field public final int displayId;
+ field public final boolean isDuplicateTouchToWallpaper;
+ field public final boolean isFocusable;
+ field public final boolean isPreventSplitting;
+ field public final boolean isTouchable;
field public final boolean isTrustedOverlay;
field public final boolean isVisible;
+ field public final boolean isWatchOutsideTouch;
field @NonNull public final String name;
field @NonNull public final android.graphics.Matrix transform;
field @NonNull public final android.os.IBinder windowToken;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4f8e8dd813a1..014ddd41f8d4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3484,9 +3484,20 @@ class ContextImpl extends Context {
// only do this if the user already has more than one preferred locale
if (android.content.res.Flags.defaultLocale()
&& r.getConfiguration().getLocales().size() > 1) {
- LocaleConfig lc = getUserId() < 0
- ? LocaleConfig.fromContextIgnoringOverride(this)
- : new LocaleConfig(this);
+ LocaleConfig lc;
+ if (getUserId() < 0) {
+ lc = LocaleConfig.fromContextIgnoringOverride(this);
+ } else {
+ // This is needed because the app might have locale config overrides that need to
+ // be read from disk in order for resources to correctly choose which values to
+ // load.
+ StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+ try {
+ lc = new LocaleConfig(this);
+ } finally {
+ StrictMode.setThreadPolicy(policy);
+ }
+ }
mResourcesManager.setLocaleConfig(lc);
}
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index df6badcffe8e..d54074818b41 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -328,7 +328,7 @@ interface IActivityTaskManager {
* A splash screen view has copied.
*/
void onSplashScreenViewCopyFinished(int taskId,
- in SplashScreenView.SplashScreenViewParcelable material);
+ in @nullable SplashScreenView.SplashScreenViewParcelable material);
/**
* When the Picture-in-picture state has changed.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 337e3f1195be..8c5773a05764 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -745,6 +745,16 @@ public class Notification implements Parcelable
@TestApi
public static final int FLAG_USER_INITIATED_JOB = 0x00008000;
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if this notification has been lifetime extended due to a direct reply.
+ *
+ * This flag is for internal use only; applications cannot set this flag directly.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public static final int FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY = 0x00010000;
+
private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index d8448dcb4f9c..772b0b46d3b4 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -85,6 +85,9 @@ per-file IEphemeralResolver.aidl = file:/services/core/java/com/android/server/p
per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+# Pinner
+per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
+
# ResourcesManager
per-file ResourcesManager.java = file:RESOURCES_OWNERS
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 6009c29ae53c..24a51573b48a 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -159,7 +159,8 @@ public class ResourcesManager {
* Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
* instance is alive and reachable.
*/
- private class ApkAssetsSupplier {
+ @VisibleForTesting
+ protected class ApkAssetsSupplier {
final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
/**
@@ -544,7 +545,10 @@ public class ResourcesManager {
* from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
* {@link #loadApkAssets(ApkKey)}.
*/
- private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+
+ @VisibleForTesting
+ @UnsupportedAppUsage
+ protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager.Builder builder = new AssetManager.Builder();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 79a5879b5cc0..9cf732abb86a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -137,6 +137,8 @@ import android.media.projection.MediaProjectionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
+import android.media.tv.ad.ITvAdManager;
+import android.media.tv.ad.TvAdManager;
import android.media.tv.interactive.ITvInteractiveAppManager;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
@@ -174,6 +176,7 @@ import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
+import android.os.ISecurityStateManager;
import android.os.ISystemUpdateManager;
import android.os.IThermalService;
import android.os.IUserManager;
@@ -182,6 +185,7 @@ import android.os.PerformanceHintManager;
import android.os.PermissionEnforcer;
import android.os.PowerManager;
import android.os.RecoverySystem;
+import android.os.SecurityStateManager;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -628,6 +632,17 @@ public final class SystemServiceRegistry {
ctx.mMainThread.getHandler());
}});
+ registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,
+ new CachedServiceFetcher<SecurityStateManager>() {
+ @Override
+ public SecurityStateManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.SECURITY_STATE_SERVICE);
+ ISecurityStateManager service = ISecurityStateManager.Stub.asInterface(b);
+ return new SecurityStateManager(service);
+ }});
+
registerService(Context.SENSOR_SERVICE, SensorManager.class,
new CachedServiceFetcher<SensorManager>() {
@Override
@@ -960,6 +975,18 @@ public final class SystemServiceRegistry {
return new TvInteractiveAppManager(service, ctx.getUserId());
}});
+ registerService(Context.TV_AD_SERVICE, TvAdManager.class,
+ new CachedServiceFetcher<TvAdManager>() {
+ @Override
+ public TvAdManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder =
+ ServiceManager.getServiceOrThrow(Context.TV_AD_SERVICE);
+ ITvAdManager service =
+ ITvAdManager.Stub.asInterface(iBinder);
+ return new TvAdManager(service, ctx.getUserId());
+ }});
+
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
new CachedServiceFetcher<TvInputManager>() {
@Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6a03c17159d3..ce1d43d10c34 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -180,6 +180,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
@Override
public void injectInputEventToInputFilter(InputEvent event) throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
mAccessibilityManager.injectInputEventToInputFilter(event);
}
diff --git a/core/java/android/app/pinner-client.aconfig b/core/java/android/app/pinner-client.aconfig
new file mode 100644
index 000000000000..b60ad9ee1f8d
--- /dev/null
+++ b/core/java/android/app/pinner-client.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "system_performance"
+ name: "pinner_service_client_api"
+ description: "Control exposing PinnerService APIs."
+ bug: "307594624"
+} \ No newline at end of file
diff --git a/core/java/android/app/pinner/IPinnerService.aidl b/core/java/android/app/pinner/IPinnerService.aidl
new file mode 100644
index 000000000000..e5d0a05dd259
--- /dev/null
+++ b/core/java/android/app/pinner/IPinnerService.aidl
@@ -0,0 +1,12 @@
+package android.app.pinner;
+
+import android.app.pinner.PinnedFileStat;
+
+/**
+ * Interface for processes to communicate with system's PinnerService.
+ * @hide
+ */
+interface IPinnerService {
+ @EnforcePermission("DUMP")
+ List<PinnedFileStat> getPinnerStats();
+} \ No newline at end of file
diff --git a/core/java/android/app/pinner/OWNERS b/core/java/android/app/pinner/OWNERS
new file mode 100644
index 000000000000..3e3fa66ca916
--- /dev/null
+++ b/core/java/android/app/pinner/OWNERS
@@ -0,0 +1,10 @@
+carmenjackson@google.com
+dualli@google.com
+edgararriaga@google.com
+kevinjeon@google.com
+philipcuadra@google.com
+shombert@google.com
+timmurray@google.com
+wessam@google.com
+jdduke@google.com
+shayba@google.com \ No newline at end of file
diff --git a/core/java/android/app/pinner/PinnedFileStat.aidl b/core/java/android/app/pinner/PinnedFileStat.aidl
new file mode 100644
index 000000000000..44217cf57d4d
--- /dev/null
+++ b/core/java/android/app/pinner/PinnedFileStat.aidl
@@ -0,0 +1,3 @@
+package android.app.pinner;
+
+parcelable PinnedFileStat; \ No newline at end of file
diff --git a/core/java/android/app/pinner/PinnedFileStat.java b/core/java/android/app/pinner/PinnedFileStat.java
new file mode 100644
index 000000000000..2e36330fc6df
--- /dev/null
+++ b/core/java/android/app/pinner/PinnedFileStat.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.pinner;
+
+import static android.app.Flags.FLAG_PINNER_SERVICE_CLIENT_API;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@TestApi
+@FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+public final class PinnedFileStat implements Parcelable {
+ private String filename;
+ private long bytesPinned;
+ private String groupName;
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public long getBytesPinned() {
+ return bytesPinned;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull String getFilename() {
+ return filename;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull String getGroupName() {
+ return groupName;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public PinnedFileStat(@NonNull String filename, long bytesPinned, @NonNull String groupName) {
+ this.filename = filename;
+ this.bytesPinned = bytesPinned;
+ this.groupName = groupName;
+ }
+
+ private PinnedFileStat(Parcel source) {
+ readFromParcel(source);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(filename);
+ dest.writeLong(bytesPinned);
+ dest.writeString8(groupName);
+ }
+
+ private void readFromParcel(@NonNull Parcel source) {
+ filename = source.readString8();
+ bytesPinned = source.readLong();
+ groupName = source.readString8();
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public static final @NonNull Creator<PinnedFileStat> CREATOR = new Creator<>() {
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public PinnedFileStat createFromParcel(Parcel source) {
+ return new PinnedFileStat(source);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public PinnedFileStat[] newArray(int size) {
+ return new PinnedFileStat[size];
+ }
+ };
+}
diff --git a/core/java/android/app/pinner/PinnerServiceClient.java b/core/java/android/app/pinner/PinnerServiceClient.java
new file mode 100644
index 000000000000..8b7c6ccb51f2
--- /dev/null
+++ b/core/java/android/app/pinner/PinnerServiceClient.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.pinner;
+
+import static android.app.Flags.FLAG_PINNER_SERVICE_CLIENT_API;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.pinner.IPinnerService;
+import android.app.pinner.PinnedFileStat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Expose PinnerService as an interface to apps.
+ * @hide
+ */
+@TestApi
+@FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+public class PinnerServiceClient {
+ private static String TAG = "PinnerServiceClient";
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public PinnerServiceClient() {}
+
+ /**
+ * Obtain the pinned file stats used for testing infrastructure.
+ * @return List of pinned files or an empty list if failed to retrieve them.
+ * @throws RuntimeException on failure to retrieve stats.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull List<PinnedFileStat> getPinnerStats() {
+ IBinder binder = ServiceManager.getService("pinner");
+ if (binder == null) {
+ Slog.w(TAG,
+ "Failed to retrieve PinnerService. A common failure reason is due to a lack of selinux permissions.");
+ return new ArrayList<>();
+ }
+ IPinnerService pinnerService = IPinnerService.Stub.asInterface(binder);
+ if (pinnerService == null) {
+ Slog.w(TAG, "Failed to cast PinnerService.");
+ return new ArrayList<>();
+ }
+ List<PinnedFileStat> stats;
+ try {
+ stats = pinnerService.getPinnerStats();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to retrieve stats from PinnerService");
+ }
+ return stats;
+ }
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index ebd5d649fc2b..cf191784b728 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -22,6 +22,7 @@ import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEventsQuery;
import android.content.pm.ParceledListSlice;
+import android.os.PersistableBundle;
/**
* System private API for talking with the UsageStatsManagerService.
@@ -77,6 +78,8 @@ interface IUsageStatsManager {
String callingPackage);
void reportUsageStop(in IBinder activity, String token, String callingPackage);
void reportUserInteraction(String packageName, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)")
+ void reportUserInteractionWithBundle(String packageName, int userId, in PersistableBundle eventExtras);
int getUsageSource();
void forceUsageSourceSettingRead();
long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
index aa3239237478..7bc6cb9f616a 100644
--- a/core/java/android/app/usage/ParcelableUsageEventList.java
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -223,6 +223,7 @@ public final class ParcelableUsageEventList implements Parcelable {
event.mContentAnnotations = null;
event.mNotificationChannelId = null;
event.mLocusId = null;
+ event.mExtras = null;
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE -> {
@@ -237,6 +238,11 @@ public final class ParcelableUsageEventList implements Parcelable {
case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
+ case Event.USER_INTERACTION -> {
+ if (in.readInt() != 0) {
+ event.mExtras = in.readPersistableBundle(getClass().getClassLoader());
+ }
+ }
}
event.mFlags = in.readInt();
@@ -263,6 +269,14 @@ public final class ParcelableUsageEventList implements Parcelable {
case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
+ case Event.USER_INTERACTION -> {
+ if (event.mExtras != null) {
+ dest.writeInt(1);
+ dest.writePersistableBundle(event.mExtras);
+ } else {
+ dest.writeInt(0);
+ }
+ }
}
dest.writeInt(event.mFlags);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6c7eba06dea7..9eb73b32a2a7 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,7 +16,9 @@
package android.app.usage;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +26,7 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -551,6 +554,22 @@ public final class UsageEvents implements Parcelable {
public int mLocusIdToken = UNASSIGNED_TOKEN;
/** @hide */
+ public PersistableBundle mExtras = null;
+
+ /** @hide */
+ public static class UserInteractionEventExtrasToken {
+ public int mCategoryToken = UNASSIGNED_TOKEN;
+ public int mActionToken = UNASSIGNED_TOKEN;
+
+ public UserInteractionEventExtrasToken() {
+ // Do nothing.
+ }
+ }
+
+ /** @hide */
+ public UserInteractionEventExtrasToken mUserInteractionExtrasToken = null;
+
+ /** @hide */
@EventFlags
public int mFlags;
@@ -650,6 +669,21 @@ public final class UsageEvents implements Parcelable {
}
/**
+ * Retrieves a map of extended data from the event if the event is of type
+ * {@link #USER_INTERACTION}.
+ *
+ * @return the map of all extras that associated with the reported user interaction
+ * event. The returned {@link PersistableBundle} will contain the extras
+ * {@link UsageStatsManager#EXTRA_EVENT_CATEGORY} and
+ * {@link UsageStatsManager#EXTRA_EVENT_ACTION}. {@link PersistableBundle#EMPTY}
+ * will be returned if the details are not available.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public @NonNull PersistableBundle getExtras() {
+ return mExtras == null ? PersistableBundle.EMPTY : mExtras;
+ }
+
+ /**
* Returns a {@link Configuration} for this event if the event is of type
* {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
*/
@@ -747,6 +781,7 @@ public final class UsageEvents implements Parcelable {
mBucketAndReason = orig.mBucketAndReason;
mNotificationChannelId = orig.mNotificationChannelId;
mLocusId = orig.mLocusId;
+ mExtras = orig.mExtras;
}
}
@@ -802,6 +837,7 @@ public final class UsageEvents implements Parcelable {
if (mEventCount != mEventsToWrite.size()) {
Log.w(TAG, "Partial usage event list received: " + mEventCount + " != "
+ mEventsToWrite.size());
+ mEventCount = mEventsToWrite.size();
}
}
@@ -987,6 +1023,14 @@ public final class UsageEvents implements Parcelable {
case Event.LOCUS_ID_SET:
p.writeString(event.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ if (event.mExtras != null) {
+ p.writeInt(1);
+ p.writePersistableBundle(event.mExtras);
+ } else {
+ p.writeInt(0);
+ }
+ break;
}
p.writeInt(event.mFlags);
}
@@ -1036,6 +1080,7 @@ public final class UsageEvents implements Parcelable {
eventOut.mContentAnnotations = null;
eventOut.mNotificationChannelId = null;
eventOut.mLocusId = null;
+ eventOut.mExtras = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -1059,6 +1104,11 @@ public final class UsageEvents implements Parcelable {
case Event.LOCUS_ID_SET:
eventOut.mLocusId = p.readString();
break;
+ case Event.USER_INTERACTION:
+ if (p.readInt() != 0) {
+ eventOut.mExtras = p.readPersistableBundle(getClass().getClassLoader());
+ }
+ break;
}
eventOut.mFlags = p.readInt();
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4f1c993bfa1a..85d223d01ee8 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -28,6 +28,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -35,6 +36,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Build;
+import android.os.PersistableBundle;
import android.os.PowerWhitelistManager;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -392,6 +394,23 @@ public final class UsageStatsManager {
@SystemApi
public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the category of the user interaction associated with the event. The category cannot
+ * be more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_CATEGORY =
+ "android.app.usage.extra.EVENT_CATEGORY";
+
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the action of the user interaction associated with the event. The action cannot be
+ * more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_ACTION =
+ "android.app.usage.extra.EVENT_ACTION";
/**
* App usage observers will consider the task root package the source of usage.
@@ -562,10 +581,10 @@ public final class UsageStatsManager {
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents}.
*/
public UsageEvents queryEvents(long beginTime, long endTime) {
@@ -611,10 +630,10 @@ public final class UsageStatsManager {
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents} object.
*
* @see #queryEvents(long, long)
@@ -1128,6 +1147,7 @@ public final class UsageStatsManager {
* Reports user interaction with a given package in the given user.
*
* <p><em>This method is only for use by the system</em>
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
@@ -1140,6 +1160,38 @@ public final class UsageStatsManager {
}
/**
+ * Reports user interaction with given package and a particular {@code extras}
+ * in the given user.
+ *
+ * <p>
+ * Note: The structure of {@code extras} is a {@link PersistableBundle} with the
+ * category {@link #EXTRA_EVENT_CATEGORY} and the action {@link #EXTRA_EVENT_ACTION}.
+ * Category provides additional detail about the user interaction, the value
+ * is defined in namespace based. Example: android.app.notification could be used to
+ * indicate that the reported user interaction is related to notification. Action
+ * indicates the general action that performed.
+ * </p>
+ *
+ * @param packageName The package name of the app
+ * @param userId The user id who triggers the user interaction
+ * @param extras The {@link PersistableBundle} that will be used to specify the
+ * extra details for the user interaction event. The {@link PersistableBundle}
+ * must contain the extras {@link #EXTRA_EVENT_CATEGORY},
+ * {@link #EXTRA_EVENT_ACTION}. Cannot be empty.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
+ public void reportUserInteraction(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ try {
+ mService.reportUserInteractionWithBundle(packageName, userId, extras);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report usage associated with a particular {@code token} has started. Tokens are app defined
* strings used to represent usage of in-app features. Apps with the {@link
* android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index c6012bbc7292..6e451479c5a4 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -52,6 +52,7 @@ import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FunctionalUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -557,11 +558,45 @@ public class AppWidgetManager {
}
}).toArray(ComponentName[]::new));
} catch (Exception e) {
- Log.e(TAG, "Nofity service of inheritance info", e);
+ Log.e(TAG, "Notify service of inheritance info", e);
}
});
}
+ private void tryAdapterConversion(
+ FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
+ RemoteViews original, String failureMsg) {
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (original != null && original.hasLegacyLists()));
+
+ if (isConvertingAdapter) {
+ final RemoteViews viewsCopy = new RemoteViews(original);
+ Runnable updateWidgetWithTask = () -> {
+ try {
+ viewsCopy.collectAllIntents().get();
+ action.acceptOrThrow(viewsCopy);
+ } catch (Exception e) {
+ Log.e(TAG, failureMsg, e);
+ }
+ };
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ createUpdateExecutorIfNull().execute(updateWidgetWithTask);
+ return;
+ }
+
+ updateWidgetWithTask.run();
+ return;
+ }
+
+ try {
+ action.acceptOrThrow(original);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -586,32 +621,8 @@ public class AppWidgetManager {
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds,
+ view), views, "Error updating app widget views in background");
}
/**
@@ -716,32 +727,9 @@ public class AppWidgetManager {
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error partially updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName,
+ appWidgetIds, view), views,
+ "Error partially updating app widget views in background");
}
/**
@@ -793,33 +781,8 @@ public class AppWidgetManager {
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetProvider(provider, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget view using provider in background",
- e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetProvider(provider, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views,
+ "Error updating app widget view using provider in background");
}
/**
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 102cbf3a7e31..3520c0b4d724 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -246,4 +246,10 @@ interface IVirtualDevice {
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterVirtualCamera(in VirtualCameraConfig camera);
+
+ /**
+ * Returns the id of the virtual camera with given config.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ int getVirtualCameraId(in VirtualCameraConfig camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index da8277c24f6c..9492a62a7f67 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -19,6 +19,7 @@ package android.companion.virtual;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.virtual.audio.VirtualAudioDevice;
@@ -340,12 +341,17 @@ public class VirtualDeviceInternal {
return mVirtualAudioDevice;
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
- return new VirtualCamera(mVirtualDevice, config);
+ try {
+ mVirtualDevice.registerVirtualCamera(config);
+ return new VirtualCamera(mVirtualDevice, config);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
- @NonNull
void setShowPointerIcon(boolean showPointerIcon) {
try {
mVirtualDevice.setShowPointerIcon(showPointerIcon);
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index beee86fcfac2..52afa4eda2f8 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -66,15 +66,8 @@ public final class VirtualCamera implements Closeable {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public VirtualCamera(
@NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
- mVirtualDevice = virtualDevice;
+ mVirtualDevice = Objects.requireNonNull(virtualDevice);
mConfig = Objects.requireNonNull(config);
- Objects.requireNonNull(virtualDevice);
- // TODO(b/310857519): Avoid registration inside constructor.
- try {
- mVirtualDevice.registerVirtualCamera(config);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
}
/** Returns the configuration of this virtual camera instance. */
@@ -83,6 +76,20 @@ public final class VirtualCamera implements Closeable {
return mConfig;
}
+ /**
+ * Returns the id of this virtual camera instance.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public String getId() {
+ try {
+ return Integer.toString(mVirtualDevice.getVirtualCameraId(mConfig));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void close() {
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 10da8b1c8203..f0477d47f723 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -32,6 +32,13 @@ flag {
}
flag {
+ name: "consistent_display_flags"
+ namespace: "virtual_devices"
+ description: "Make virtual display flags consistent with display manager"
+ bug: "300905478"
+}
+
+flag {
name: "vdm_custom_home"
namespace: "virtual_devices"
description: "Enable custom home API"
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 4b2cee698df2..697c25c2a1ec 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -730,10 +730,7 @@ public final class AttributionSource implements Parcelable {
/**
* The next app to receive the permission protected data.
- *
- * @deprecated Use {@link #setNextAttributionSource} instead.
*/
- @Deprecated
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c6c7b5baa58..b75c64dcc3c1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -72,6 +72,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Flags;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -4214,6 +4215,7 @@ public abstract class Context {
DEVICE_LOCK_SERVICE,
VIRTUALIZATION_SERVICE,
GRAMMATICAL_INFLECTION_SERVICE,
+ SECURITY_STATE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
@@ -5818,6 +5820,17 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.tv.ad.TvAdManager} for interacting with TV client-side advertisement
+ * services on the device.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.tv.ad.TvAdManager
+ */
+ @FlaggedApi(android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW)
+ public static final String TV_AD_SERVICE = "tv_ad";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.media.tv.TunerResourceManager} for interacting with TV
* tuner resources on the device.
*
@@ -6478,6 +6491,16 @@ public abstract class Context {
public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.SecurityStateManager} for accessing the security state manager service.
+ *
+ * @see #getSystemService(String)
+ * @see android.os.SecurityStateManager
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ public static final String SECURITY_STATE_SERVICE = "security_state";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c7a86fbe0171..38bcfa220af4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5353,11 +5353,11 @@ public class Intent implements Parcelable, Cloneable {
* Broadcast Action: Sent to the responsible installer of an archived package when unarchival
* is requested.
*
- * @see android.content.pm.PackageInstaller#requestUnarchive(String)
- * @hide
+ * @see android.content.pm.PackageInstaller#requestUnarchive
*/
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
+ @BroadcastBehavior(explicitOnly = true)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
// ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 1cfdb8b37fcd..5736a6d8cb4a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -20,7 +20,6 @@ import android.annotation.CurrentTimeMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -522,9 +521,7 @@ public class PackageInfo implements Parcelable {
/**
* Returns the time at which the app was archived for the user. Units are as
* per {@link System#currentTimeMillis()}.
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public @CurrentTimeMillisLong long getArchiveTimeMillis() {
return mArchiveTimeMillis;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 6df1f600c3ef..d35c3922e9b7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -354,10 +354,7 @@ public class PackageInstaller {
/**
* Extra field for the package name of a package that is requested to be unarchived. Sent as
* part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_PACKAGE_NAME =
"android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
@@ -366,22 +363,16 @@ public class PackageInstaller {
* Extra field for the unarchive ID. Sent as
* part of the {@link android.content.Intent#ACTION_UNARCHIVE_PACKAGE} intent.
*
- * @see Session#setUnarchiveId(int)
- *
- * @hide
+ * @see SessionParams#setUnarchiveId
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_ID =
"android.content.pm.extra.UNARCHIVE_ID";
/**
* If true, the requestor of the unarchival has specified that the app should be unarchived
- * for {@link android.os.UserHandle#ALL}.
- *
- * @hide
+ * for all users.
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_ALL_USERS =
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
@@ -398,9 +389,7 @@ public class PackageInstaller {
* failure dialog.
*
* @see #requestUnarchive
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS";
@@ -675,10 +664,7 @@ public class PackageInstaller {
*
* <p> Note that this does not mean that the unarchival has completed. This status should be
* sent before any longer asynchronous action (e.g. app download) is started.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_OK = 0;
@@ -687,10 +673,7 @@ public class PackageInstaller {
*
* <p> An example use case for this could be that the user needs to login to allow the
* download for a paid app.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1;
@@ -700,19 +683,13 @@ public class PackageInstaller {
* <p> The installer can optionally provide a {@code userActionIntent} for a space-clearing
* dialog. If no action is provided, then a generic intent
* {@link android.os.storage.StorageManager#ACTION_MANAGE_STORAGE} is started instead.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2;
/**
* The device is not connected to the internet
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3;
@@ -720,10 +697,7 @@ public class PackageInstaller {
* The installer responsible for the unarchival is disabled.
*
* <p> Should only be used by the system.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4;
@@ -731,19 +705,13 @@ public class PackageInstaller {
* The installer responsible for the unarchival has been uninstalled
*
* <p> Should only be used by the system.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5;
/**
* Generic error: The app cannot be unarchived.
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public static final int UNARCHIVAL_GENERIC_ERROR = 100;
@@ -2364,12 +2332,10 @@ public class PackageInstaller {
* @param statusReceiver Callback used to notify when the operation is completed.
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* available to the caller or isn't archived.
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.REQUEST_DELETE_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
throws PackageManager.NameNotFoundException {
@@ -2395,19 +2361,16 @@ public class PackageInstaller {
*
* @param statusReceiver Callback used to notify whether the installer has accepted the
* unarchival request or an error has occurred. The status update will be
- * sent though {@link EXTRA_UNARCHIVE_STATUS}. Only one status will be
+ * sent though {@link #EXTRA_UNARCHIVE_STATUS}. Only one status will be
* sent.
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* visible to the caller or if the package has no
* installer on the device anymore to unarchive it.
* @throws IOException If parameters were unsatisfiable, such as lack of disk space.
- *
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.REQUEST_INSTALL_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void requestUnarchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
throws IOException, PackageManager.NameNotFoundException {
@@ -2435,12 +2398,10 @@ public class PackageInstaller {
* @param userActionIntent Optional intent to start a follow up action required to
* facilitate the unarchival flow (e.g. user needs to log in).
* @throws PackageManager.NameNotFoundException if no unarchival with {@code unarchiveId} exists
- * @hide
*/
@RequiresPermission(anyOf = {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.REQUEST_INSTALL_PACKAGES})
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public void reportUnarchivalStatus(int unarchiveId, @UnarchivalStatus int status,
long requiredStorageBytes, @Nullable PendingIntent userActionIntent)
@@ -3454,11 +3415,8 @@ public class PackageInstaller {
* <p> The ID should be retrieved from the unarchive intent and passed into the
* session that's being created to unarchive the app in question. Used to link the unarchive
* intent and the install session to disambiguate.
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_ARCHIVING)
- @SystemApi
public void setUnarchiveId(int unarchiveId) {
this.unarchiveId = unarchiveId;
}
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index c7091ad99199..70e6f9864eb6 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -177,12 +177,10 @@ public class PackageItemInfo {
/**
* Whether the package is currently in an archived state.
*
- * <p>Packages can be archived through {@link PackageArchiver} and do not have any APKs stored
- * on the device, but do keep the data directory.
- * @hide
+ * <p>Packages can be archived through {@link PackageInstaller#requestArchive} and do not have
+ * any APKs stored on the device, but do keep the data directory.
+ *
*/
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
@FlaggedApi(Flags.FLAG_ARCHIVING)
public boolean isArchived;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6775f9b8d84d..a22fe3f1452b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1263,18 +1263,14 @@ public abstract class PackageManager {
/**
* Flag parameter to also retrieve some information about archived packages.
- * Packages can be archived through
- * {@link PackageInstaller#requestArchive(String, IntentSender)} and do not have any APKs stored
- * on the device, but do keep the data directory.
+ * Packages can be archived through {@link PackageInstaller#requestArchive} and do not have any
+ * APKs stored on the device, but do keep the data directory.
* <p> Note: Archived apps are a subset of apps returned by {@link #MATCH_UNINSTALLED_PACKAGES}.
* <p> Note: this flag may cause less information about currently installed
* applications to be returned.
* <p> Note: use of this flag requires the android.permission.QUERY_ALL_PACKAGES
* permission to see uninstalled packages.
- * @hide
*/
- // TODO(b/278553670) Unhide and update @links before launch.
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
@@ -8969,10 +8965,7 @@ public abstract class PackageManager {
*
* @throws NameNotFoundException if the given package name is not available to the caller.
* @see PackageInstaller#requestArchive(String, IntentSender)
- *
- * @hide
*/
- @SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isAppArchivable not implemented");
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index a565f6825e7a..d21b81854584 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -94,3 +94,10 @@ flag {
description: "Feature flag to read install related information from an APK."
bug: "275658500"
}
+
+flag {
+ name: "use_pia_v2"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the refactored Package Installer app with updated UI."
+ bug: "182205982"
+}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 1b8eb0748737..40592a151fa7 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -15,3 +15,12 @@ flag {
description: "Feature flag for passing in an AssetFileDescriptor to create an frro"
bug: "304478666"
}
+
+flag {
+ name: "manifest_flagging"
+ namespace: "resource_manager"
+ description: "Feature flag for flagging manifest entries"
+ bug: "297373084"
+ # This flag is read in PackageParser at boot time, and in aapt2 which is a build tool.
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index d786d9a20189..18c95bfbb297 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -66,6 +66,13 @@ public final class SensorPrivacyManager {
+ ".extra.sensor";
/**
+ * An extra containing the notification id that triggered the intent
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_ID = SensorPrivacyManager.class.getName()
+ + ".extra.notification_id";
+
+ /**
* An extra indicating if all sensors are affected
* @hide
*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index dfc27caa362c..507e8140ff61 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +25,8 @@ import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.util.Log;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index d4ce0ebbc528..5cbb0bbadc4c 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,8 +16,6 @@
package android.hardware.camera2.params;
-import static com.android.internal.R.string.hardware;
-
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ca84b3563561..6a83cee10309 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -682,6 +682,7 @@ public abstract class BatteryConsumer {
static class BatteryConsumerDataLayout {
private static final Key[] KEY_ARRAY = new Key[0];
+ public static final int POWER_MODEL_NOT_INCLUDED = -1;
public final String[] customPowerComponentNames;
public final int customPowerComponentCount;
public final boolean powerModelsIncluded;
@@ -713,7 +714,9 @@ public abstract class BatteryConsumer {
// Declare the Key for the power component, ignoring other dimensions.
perComponentKeys.add(
new Key(componentId, PROCESS_STATE_ANY,
- powerModelsIncluded ? columnIndex++ : -1, // power model
+ powerModelsIncluded
+ ? columnIndex++
+ : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -736,7 +739,9 @@ public abstract class BatteryConsumer {
perComponentKeys.add(
new Key(componentId, processState,
- powerModelsIncluded ? columnIndex++ : -1, // power model
+ powerModelsIncluded
+ ? columnIndex++
+ : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -843,11 +848,27 @@ public abstract class BatteryConsumer {
@SuppressWarnings("unchecked")
@NonNull
+ public T addConsumedPower(@PowerComponent int componentId, double componentPower,
+ @PowerModel int powerModel) {
+ mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentPower, powerModel);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel);
return (T) this;
}
+ @SuppressWarnings("unchecked")
+ @NonNull
+ public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
+ mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel);
+ return (T) this;
+ }
+
/**
* Sets the amount of drain attributed to the specified custom drain type.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e85b7bf1c91e..16ffaef03121 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4029,6 +4029,17 @@ public abstract class BatteryStats {
}
/**
+ * A helper object passed to various dump... methods to integrate with such objects
+ * as BatteryUsageStatsProvider.
+ */
+ public interface BatteryStatsDumpHelper {
+ /**
+ * Generates BatteryUsageStats based on the specified BatteryStats.
+ */
+ BatteryUsageStats getBatteryUsageStats(BatteryStats batteryStats, boolean detailed);
+ }
+
+ /**
* Dumps the ControllerActivityCounter if it has any data worth dumping.
* The order of the arguments in the final check in line is:
*
@@ -4390,7 +4401,7 @@ public abstract class BatteryStats {
* NOTE: all times are expressed in microseconds, unless specified otherwise.
*/
public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
- boolean wifiOnly) {
+ boolean wifiOnly, BatteryStatsDumpHelper dumpHelper) {
if (which != BatteryStats.STATS_SINCE_CHARGED) {
dumpLine(pw, 0, STAT_NAMES[which], "err",
@@ -4652,7 +4663,7 @@ public abstract class BatteryStats {
}
}
- final BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+ final BatteryUsageStats stats = dumpHelper.getBatteryUsageStats(this, true /* detailed */);
dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
formatCharge(stats.getBatteryCapacity()),
formatCharge(stats.getConsumedPower()),
@@ -5127,7 +5138,7 @@ public abstract class BatteryStats {
@SuppressWarnings("unused")
public final void dumpLocked(Context context, PrintWriter pw, String prefix, final int which,
- int reqUid, boolean wifiOnly) {
+ int reqUid, boolean wifiOnly, BatteryStatsDumpHelper dumpHelper) {
if (which != BatteryStats.STATS_SINCE_CHARGED) {
pw.println("ERROR: BatteryStats.dump called for which type " + which
@@ -5854,7 +5865,7 @@ public abstract class BatteryStats {
pw.println();
- BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+ BatteryUsageStats stats = dumpHelper.getBatteryUsageStats(this, true /* detailed */);
stats.dump(pw, prefix);
List<UidMobileRadioStats> uidMobileRadioStats =
@@ -7642,10 +7653,11 @@ public abstract class BatteryStats {
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
- * @param pw a Printer to receive the dump output.
+ * @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart,
+ BatteryStatsDumpHelper dumpHelper) {
synchronized (this) {
prepareForDumpLocked();
}
@@ -7663,12 +7675,12 @@ public abstract class BatteryStats {
}
synchronized (this) {
- dumpLocked(context, pw, flags, reqUid, filtering);
+ dumpLocked(context, pw, flags, reqUid, filtering, dumpHelper);
}
}
private void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid,
- boolean filtering) {
+ boolean filtering, BatteryStatsDumpHelper dumpHelper) {
if (!filtering) {
SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
@@ -7803,15 +7815,15 @@ public abstract class BatteryStats {
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+ (flags & DUMP_DEVICE_WIFI_ONLY) != 0, dumpHelper);
pw.println();
}
}
// This is called from BatteryStatsService.
@SuppressWarnings("unused")
- public void dumpCheckin(Context context, PrintWriter pw,
- List<ApplicationInfo> apps, int flags, long histStart) {
+ public void dumpCheckin(Context context, PrintWriter pw, List<ApplicationInfo> apps, int flags,
+ long histStart, BatteryStatsDumpHelper dumpHelper) {
synchronized (this) {
prepareForDumpLocked();
@@ -7829,12 +7841,12 @@ public abstract class BatteryStats {
}
synchronized (this) {
- dumpCheckinLocked(context, pw, apps, flags);
+ dumpCheckinLocked(context, pw, apps, flags, dumpHelper);
}
}
private void dumpCheckinLocked(Context context, PrintWriter pw, List<ApplicationInfo> apps,
- int flags) {
+ int flags, BatteryStatsDumpHelper dumpHelper) {
if (apps != null) {
SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
for (int i=0; i<apps.size(); i++) {
@@ -7881,7 +7893,7 @@ public abstract class BatteryStats {
(Object[])lineArgs);
}
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+ (flags & DUMP_DEVICE_WIFI_ONLY) != 0, dumpHelper);
}
}
@@ -7891,7 +7903,7 @@ public abstract class BatteryStats {
* @hide
*/
public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
- int flags, long histStart) {
+ int flags, long histStart, BatteryStatsDumpHelper dumpHelper) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
prepareForDumpLocked();
@@ -7909,7 +7921,8 @@ public abstract class BatteryStats {
proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
if ((flags & DUMP_DAILY_ONLY) == 0) {
- final BatteryUsageStats stats = getBatteryUsageStats(context, false /* detailed */);
+ final BatteryUsageStats stats =
+ dumpHelper.getBatteryUsageStats(this, false /* detailed */);
ProportionalAttributionCalculator proportionalAttributionCalculator =
new ProportionalAttributionCalculator(context, stats);
dumpProtoAppsLocked(proto, stats, apps, proportionalAttributionCalculator);
@@ -8856,8 +8869,6 @@ public abstract class BatteryStats {
return !tm.isDataCapable();
}
- protected abstract BatteryUsageStats getBatteryUsageStats(Context context, boolean detailed);
-
private boolean shouldHidePowerComponent(int powerComponent) {
return powerComponent == BatteryConsumer.POWER_COMPONENT_IDLE
|| powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index cd52b5c0f7f3..eabe13b0c54f 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -36,6 +36,7 @@ import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -122,6 +123,12 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+ private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+ BatteryConsumer.PROCESS_STATE_FOREGROUND,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+ };
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
@@ -515,6 +522,22 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
proto.write(
BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_BACKGROUND_MILLIS,
bgMs);
+ for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+ final long timeInStateMillis = consumer.getTimeInProcessStateMs(processState);
+ if (timeInStateMillis <= 0) {
+ continue;
+ }
+ final long timeInStateToken = proto.start(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TIME_IN_STATE);
+ proto.write(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState.PROCESS_STATE,
+ processState);
+ proto.write(
+ BatteryUsageStatsAtomsProto.UidBatteryConsumer.TimeInState
+ .TIME_IN_STATE_MILLIS,
+ timeInStateMillis);
+ proto.end(timeInStateToken);
+ }
proto.end(token);
if (proto.getRawSize() >= maxRawSize) {
@@ -586,7 +609,8 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
+ "(" + BatteryConsumer.processStateToString(key.processState) + ")";
}
printPowerComponent(pw, prefix, label, devicePowerMah, appsPowerMah,
- deviceConsumer.getPowerModel(key),
+ mIncludesPowerModels ? deviceConsumer.getPowerModel(key)
+ : BatteryConsumer.POWER_MODEL_UNDEFINED,
deviceConsumer.getUsageDurationMillis(key));
}
}
@@ -774,6 +798,15 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
super.finalize();
}
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ dump(pw, "");
+ pw.flush();
+ return sw.toString();
+ }
+
/**
* Builder for BatteryUsageStats.
*/
diff --git a/core/java/android/os/ISecurityStateManager.aidl b/core/java/android/os/ISecurityStateManager.aidl
new file mode 100644
index 000000000000..8ae624d0371d
--- /dev/null
+++ b/core/java/android/os/ISecurityStateManager.aidl
@@ -0,0 +1,26 @@
+/* //device/java/android/android/os/ISecurityStateManager.aidl
+**
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISecurityStateManager {
+ Bundle getGlobalSecurityState();
+}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index da647e2b78cb..161951ead77f 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -437,6 +438,7 @@ public final class Message implements Parcelable {
* @see #getData()
* @see #setData(Bundle)
*/
+ @Nullable
public Bundle peekData() {
return data;
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 9e5f5399301c..9c11ad433b8f 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -15,6 +15,7 @@
*/
package android.os;
+import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
@@ -118,7 +119,7 @@ class PowerComponents {
@BatteryConsumer.PowerModel
int getPowerModel(BatteryConsumer.Key key) {
- if (key.mPowerModelColumnIndex == -1) {
+ if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
throw new IllegalStateException(
"Power model IDs were not requested in the BatteryUsageStatsQuery");
}
@@ -468,7 +469,7 @@ class PowerComponents {
mMinConsumedPowerThreshold = minConsumedPowerThreshold;
for (BatteryConsumer.Key[] keys : mData.layout.keys) {
for (BatteryConsumer.Key key : keys) {
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED);
}
}
@@ -478,11 +479,19 @@ class PowerComponents {
@NonNull
public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
int powerModel) {
- if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
- componentPower = 0;
- }
mData.putDouble(key.mPowerColumnIndex, componentPower);
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
+ mData.putInt(key.mPowerModelColumnIndex, powerModel);
+ }
+ return this;
+ }
+
+ @NonNull
+ public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower,
+ int powerModel) {
+ mData.putDouble(key.mPowerColumnIndex,
+ mData.getDouble(key.mPowerColumnIndex) + componentPower);
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
mData.putInt(key.mPowerModelColumnIndex, powerModel);
}
return this;
@@ -496,9 +505,6 @@ class PowerComponents {
*/
@NonNull
public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
- if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
- componentPower = 0;
- }
final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
if (index < 0 || index >= mData.layout.customPowerComponentCount) {
throw new IllegalArgumentException(
@@ -575,12 +581,12 @@ class PowerComponents {
mData.getLong(key.mDurationColumnIndex)
+ otherData.getLong(otherKey.mDurationColumnIndex));
- if (key.mPowerModelColumnIndex == -1) {
+ if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
continue;
}
boolean undefined = false;
- if (otherKey.mPowerModelColumnIndex == -1) {
+ if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
undefined = true;
} else {
final int powerModel = mData.getInt(key.mPowerModelColumnIndex);
@@ -641,19 +647,26 @@ class PowerComponents {
*/
@NonNull
public PowerComponents build() {
- mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
-
for (BatteryConsumer.Key[] keys : mData.layout.keys) {
for (BatteryConsumer.Key key : keys) {
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) {
mData.putInt(key.mPowerModelColumnIndex,
BatteryConsumer.POWER_MODEL_UNDEFINED);
}
}
+
+ if (mMinConsumedPowerThreshold != 0) {
+ if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) {
+ mData.putDouble(key.mPowerColumnIndex, 0);
+ }
+ }
}
}
+ if (mData.getDouble(mData.layout.totalConsumedPowerColumnIndex) == 0) {
+ mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
+ }
return new PowerComponents(this);
}
}
diff --git a/core/java/android/os/SecurityStateManager.java b/core/java/android/os/SecurityStateManager.java
new file mode 100644
index 000000000000..4fa61e0ca782
--- /dev/null
+++ b/core/java/android/os/SecurityStateManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * SecurityStateManager provides the functionality to query the security status of the system and
+ * platform components. For example, this includes the system and vendor security patch level.
+ */
+@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+@SystemService(Context.SECURITY_STATE_SERVICE)
+public class SecurityStateManager {
+
+ /**
+ * The system SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_SYSTEM_SPL = "system_spl";
+
+ /**
+ * The vendor SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_VENDOR_SPL = "vendor_spl";
+
+ /**
+ * The kernel version key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_KERNEL_VERSION = "kernel_version";
+
+ private final ISecurityStateManager mService;
+
+ /**
+ * @hide
+ */
+ public SecurityStateManager(ISecurityStateManager service) {
+ mService = requireNonNull(service, "missing ISecurityStateManager");
+ }
+
+ /**
+ * Returns the current global security state. Each key-value pair is a mapping of a component
+ * of the global security state to its current version/SPL (security patch level). For example,
+ * the {@code KEY_SYSTEM_SPL} key will map to the SPL of the system as defined in
+ * {@link android.os.Build.VERSION}. The bundle will also include mappings from WebView packages
+ * and packages listed under config {@code config_securityStatePackages} to their respective
+ * versions as defined in {@link android.content.pm.PackageInfo#versionName}.
+ *
+ * @return A {@code Bundle} that contains the global security state information as
+ * string-to-string key-value pairs.
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ @NonNull
+ public Bundle getGlobalSecurityState() {
+ try {
+ return mService.getGlobalSecurityState();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a78f221fc962..c085f334457a 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -57,6 +57,13 @@ flag {
}
flag {
+ name: "security_state_service"
+ namespace: "dynamic_spl"
+ description: "Guards the Security State API."
+ bug: "302189431"
+}
+
+flag {
name: "battery_saver_supported_check_api"
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6853892348d9..78a12f75a508 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1738,23 +1738,6 @@ public class StorageManager {
return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
}
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Deprecated
- public static boolean isFileEncryptedNativeOnly() {
- return isFileEncrypted();
- }
-
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @Deprecated
- public static boolean isFileEncryptedNativeOrEmulated() {
- return isFileEncrypted();
- }
-
/** {@hide} */
public static boolean hasAdoptable() {
switch (SystemProperties.get(PROP_ADOPTABLE)) {
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index dc86e3f57c40..5cbc18e22386 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -56,4 +56,11 @@ flag {
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
bug: "222650148"
-} \ No newline at end of file
+}
+
+flag {
+ name: "factory_reset_prep_permission_apis"
+ namespace: "wallet_integration"
+ description: "enable Permission PREPARE_FACTORY_RESET."
+ bug: "302016478"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ff6ec29bb8ac..8f18c5f58c7e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14975,6 +14975,38 @@ public final class Settings {
public static final String APP_OPS_CONSTANTS = "app_ops_constants";
/**
+ * Device Idle (Doze) specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "inactive_to=60000,sensing_to=400000"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * inactive_to (long)
+ * sensing_to (long)
+ * motion_inactive_to (long)
+ * idle_after_inactive_to (long)
+ * idle_pending_to (long)
+ * max_idle_pending_to (long)
+ * idle_pending_factor (float)
+ * quick_doze_delay_to (long)
+ * idle_to (long)
+ * max_idle_to (long)
+ * idle_factor (float)
+ * min_time_to_alarm (long)
+ * max_temp_app_whitelist_duration (long)
+ * notification_whitelist_duration (long)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.DeviceIdleController.Constants
+ */
+ public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
+
+ /**
* Battery Saver specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 33a67aed6023..533d459e532b 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,4 +8,4 @@ per-file *NetworkSecurityPolicy.java = file:net/OWNERS
per-file Confirmation*.java = file:/keystore/OWNERS
per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
-per-file *.aconfig = victorhsieh@google.com
+per-file *.aconfig = victorhsieh@google.com,eranm@google.com
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 2a4cbaf79a75..46ea158b8f90 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -15,7 +15,6 @@
*/
package android.service.notification;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.os.Bundle;
@@ -26,6 +25,7 @@ import android.system.ErrnoException;
import android.system.OsConstants;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.nio.ByteBuffer;
@@ -75,10 +75,6 @@ public class NotificationRankingUpdate implements Parcelable {
}
// We only need read-only access to the shared memory region.
buffer = mRankingMapFd.mapReadOnly();
- if (buffer == null) {
- mRankingMap = null;
- return;
- }
byte[] payload = new byte[buffer.remaining()];
buffer.get(payload);
mapParcel.unmarshall(payload, 0, payload.length);
@@ -98,7 +94,7 @@ public class NotificationRankingUpdate implements Parcelable {
} finally {
mapParcel.recycle();
if (buffer != null && mRankingMapFd != null) {
- mRankingMapFd.unmap(buffer);
+ SharedMemory.unmap(buffer);
mRankingMapFd.close();
}
}
@@ -210,6 +206,7 @@ public class NotificationRankingUpdate implements Parcelable {
new NotificationListenerService.Ranking[0]
)
);
+ ByteBuffer buffer = null;
try {
// Parcels the ranking map and measures its size.
@@ -217,13 +214,10 @@ public class NotificationRankingUpdate implements Parcelable {
int mapSize = mapParcel.dataSize();
// Creates a new SharedMemory object with enough space to hold the ranking map.
- SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
- if (mRankingMapFd == null) {
- return;
- }
+ mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
// Gets a read/write buffer mapping the entire shared memory region.
- final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
+ buffer = mRankingMapFd.mapReadWrite();
// Puts the ranking map into the shared memory region buffer.
buffer.put(mapParcel.marshall(), 0, mapSize);
// Protects the region from being written to, by setting it to be read-only.
@@ -238,6 +232,12 @@ public class NotificationRankingUpdate implements Parcelable {
throw new RuntimeException(e);
} finally {
mapParcel.recycle();
+ // To prevent memory leaks, we can close the ranking map fd here.
+ // Because a reference to this still exists
+ if (buffer != null && mRankingMapFd != null) {
+ SharedMemory.unmap(buffer);
+ mRankingMapFd.close();
+ }
}
} else {
out.writeParcelable(mRankingMap, flags);
@@ -247,7 +247,7 @@ public class NotificationRankingUpdate implements Parcelable {
/**
* @hide
*/
- public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
+ public static final @NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
= new Parcelable.Creator<NotificationRankingUpdate>() {
public NotificationRankingUpdate createFromParcel(Parcel parcel) {
return new NotificationRankingUpdate(parcel);
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index e5ad85cb526f..07367df7bc91 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -15,10 +15,12 @@
*/
package android.service.notification;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.Flags;
import android.app.RemoteInput;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,6 +38,7 @@ public final class NotificationStats implements Parcelable {
private boolean mSeen;
private boolean mExpanded;
private boolean mDirectReplied;
+ private boolean mSmartReplied;
private boolean mSnoozed;
private boolean mViewedSettings;
private boolean mInteracted;
@@ -125,6 +128,9 @@ public final class NotificationStats implements Parcelable {
mSeen = in.readByte() != 0;
mExpanded = in.readByte() != 0;
mDirectReplied = in.readByte() != 0;
+ if (Flags.lifetimeExtensionRefactor()) {
+ mSmartReplied = in.readByte() != 0;
+ }
mSnoozed = in.readByte() != 0;
mViewedSettings = in.readByte() != 0;
mInteracted = in.readByte() != 0;
@@ -137,6 +143,9 @@ public final class NotificationStats implements Parcelable {
dest.writeByte((byte) (mSeen ? 1 : 0));
dest.writeByte((byte) (mExpanded ? 1 : 0));
dest.writeByte((byte) (mDirectReplied ? 1 : 0));
+ if (Flags.lifetimeExtensionRefactor()) {
+ dest.writeByte((byte) (mSmartReplied ? 1 : 0));
+ }
dest.writeByte((byte) (mSnoozed ? 1 : 0));
dest.writeByte((byte) (mViewedSettings ? 1 : 0));
dest.writeByte((byte) (mInteracted ? 1 : 0));
@@ -210,6 +219,23 @@ public final class NotificationStats implements Parcelable {
}
/**
+ * Returns whether the user has replied to a notification that has a smart reply at least once.
+ */
+ @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public boolean hasSmartReplied() {
+ return mSmartReplied;
+ }
+
+ /**
+ * Records that the user has replied to a notification that has a smart reply at least once.
+ */
+ @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void setSmartReplied() {
+ mSmartReplied = true;
+ mInteracted = true;
+ }
+
+ /**
* Returns whether the user has snoozed this notification at least once.
*/
public boolean hasSnoozed() {
@@ -286,6 +312,9 @@ public final class NotificationStats implements Parcelable {
if (mSeen != that.mSeen) return false;
if (mExpanded != that.mExpanded) return false;
if (mDirectReplied != that.mDirectReplied) return false;
+ if (Flags.lifetimeExtensionRefactor()) {
+ if (mSmartReplied != that.mSmartReplied) return false;
+ }
if (mSnoozed != that.mSnoozed) return false;
if (mViewedSettings != that.mViewedSettings) return false;
if (mInteracted != that.mInteracted) return false;
@@ -297,6 +326,9 @@ public final class NotificationStats implements Parcelable {
int result = (mSeen ? 1 : 0);
result = 31 * result + (mExpanded ? 1 : 0);
result = 31 * result + (mDirectReplied ? 1 : 0);
+ if (Flags.lifetimeExtensionRefactor()) {
+ result = 31 * result + (mSmartReplied ? 1 : 0);
+ }
result = 31 * result + (mSnoozed ? 1 : 0);
result = 31 * result + (mViewedSettings ? 1 : 0);
result = 31 * result + (mInteracted ? 1 : 0);
@@ -311,6 +343,9 @@ public final class NotificationStats implements Parcelable {
sb.append("mSeen=").append(mSeen);
sb.append(", mExpanded=").append(mExpanded);
sb.append(", mDirectReplied=").append(mDirectReplied);
+ if (Flags.lifetimeExtensionRefactor()) {
+ sb.append(", mSmartReplied=").append(mSmartReplied);
+ }
sb.append(", mSnoozed=").append(mSnoozed);
sb.append(", mViewedSettings=").append(mViewedSettings);
sb.append(", mInteracted=").append(mInteracted);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0063d13cb3ab..886727ea43ef 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -490,16 +490,32 @@ public class TelephonyRegistryManager {
/**
* Notify changes to activity state changes on certain subscription.
*
+ * @param subId for which data activity state changed.
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
+ * TelephonyManager#DATA_ACTIVITY_IN}
+ */
+ public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
+ try {
+ sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify changes to activity state changes on certain subscription.
+ *
* @param slotIndex for which data activity changed. Can be derived from subId except
* when subId is invalid.
* @param subId for which data activity state changed.
- * @param dataActivityType indicates the latest data activity type e.g, {@link
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
* TelephonyManager#DATA_ACTIVITY_IN}
*/
public void notifyDataActivityChanged(int slotIndex, int subId,
@DataActivityType int dataActivityType) {
try {
- sRegistry.notifyDataActivityForSubscriber(slotIndex, subId, dataActivityType);
+ sRegistry.notifyDataActivityForSubscriberWithSlot(slotIndex, subId, dataActivityType);
} catch (RemoteException ex) {
// system process is dead
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9f886c826174..d131dc9a4c7d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -69,6 +69,7 @@ public final class InputDevice implements Parcelable {
private final String mName;
private final int mVendorId;
private final int mProductId;
+ private final int mDeviceBus;
private final String mDescriptor;
private final InputDeviceIdentifier mIdentifier;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -468,8 +469,8 @@ public final class InputDevice implements Parcelable {
* Called by native code
*/
private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
- int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
- KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
+ int productId, int deviceBus, String descriptor, boolean isExternal, int sources,
+ int keyboardType, KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
@Nullable String keyboardLayoutType, boolean hasVibrator, boolean hasMicrophone,
boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery, int usiVersionMajor,
int usiVersionMinor, int associatedDisplayId) {
@@ -479,6 +480,7 @@ public final class InputDevice implements Parcelable {
mName = name;
mVendorId = vendorId;
mProductId = productId;
+ mDeviceBus = deviceBus;
mDescriptor = descriptor;
mIsExternal = isExternal;
mSources = sources;
@@ -512,6 +514,7 @@ public final class InputDevice implements Parcelable {
mName = in.readString();
mVendorId = in.readInt();
mProductId = in.readInt();
+ mDeviceBus = in.readInt();
mDescriptor = in.readString();
mIsExternal = in.readInt() != 0;
mSources = in.readInt();
@@ -551,6 +554,7 @@ public final class InputDevice implements Parcelable {
private String mName = "";
private int mVendorId = 0;
private int mProductId = 0;
+ private int mDeviceBus = 0;
private String mDescriptor = "";
private boolean mIsExternal = false;
private int mSources = 0;
@@ -604,6 +608,12 @@ public final class InputDevice implements Parcelable {
return this;
}
+ /** @see InputDevice#getDeviceBus() */
+ public Builder setDeviceBus(int deviceBus) {
+ mDeviceBus = deviceBus;
+ return this;
+ }
+
/** @see InputDevice#getDescriptor() */
public Builder setDescriptor(String descriptor) {
mDescriptor = descriptor;
@@ -705,6 +715,7 @@ public final class InputDevice implements Parcelable {
mName,
mVendorId,
mProductId,
+ mDeviceBus,
mDescriptor,
mIsExternal,
mSources,
@@ -847,6 +858,21 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the device bus used by given device, if available.
+ * <p>
+ * The device bus is the communication system used for transferring data
+ * (e.g. USB, Bluetooth etc.). This value comes from the kernel (from input.h).
+ * A value of 0 will be assigned where the device bus is not available.
+ * </p>
+ *
+ * @return The device bus of a given device
+ * @hide
+ */
+ public int getDeviceBus() {
+ return mDeviceBus;
+ }
+
+ /**
* Gets the input device descriptor, which is a stable identifier for an input device.
* <p>
* An input device descriptor uniquely identifies an input device. Its value
@@ -1448,6 +1474,7 @@ public final class InputDevice implements Parcelable {
out.writeString(mName);
out.writeInt(mVendorId);
out.writeInt(mProductId);
+ out.writeInt(mDeviceBus);
out.writeString(mDescriptor);
out.writeInt(mIsExternal ? 1 : 0);
out.writeInt(mSources);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8befe8a2be85..cbbe7856178d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -503,9 +503,6 @@ public final class SurfaceControl implements Parcelable {
// be dumped as additional context
private static volatile boolean sDebugUsageAfterRelease = false;
- static GlobalTransactionWrapper sGlobalTransaction;
- static long sTransactionNestCount = 0;
-
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(SurfaceControl.class.getClassLoader(),
nativeGetNativeSurfaceControlFinalizer());
@@ -859,33 +856,47 @@ public final class SurfaceControl implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_SELECTION_STRATEGY_"},
- value = {FRAME_RATE_SELECTION_STRATEGY_SELF,
- FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN})
+ value = {FRAME_RATE_SELECTION_STRATEGY_PROPAGATE,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN,
+ FRAME_RATE_SELECTION_STRATEGY_SELF})
public @interface FrameRateSelectionStrategy {}
// From window.h. Keep these in sync.
/**
* Default value. The layer uses its own frame rate specifications, assuming it has any
- * specifications, instead of its parent's.
+ * specifications, instead of its parent's. If it does not have its own frame rate
+ * specifications, it will try to use its parent's. It will propagate its specifications to any
+ * descendants that do not have their own.
+ *
* However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor layer
- * supersedes this behavior, meaning that this layer will inherit the frame rate specifications
- * of that ancestor layer.
+ * supersedes this behavior, meaning that this layer will inherit frame rate specifications
+ * regardless of whether it has its own.
* @hide
*/
- public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 0;
+ public static final int FRAME_RATE_SELECTION_STRATEGY_PROPAGATE = 0;
/**
* The layer's frame rate specifications will propagate to and override those of its descendant
* layers.
- * The layer with this strategy has the {@link #FRAME_RATE_SELECTION_STRATEGY_SELF} behavior
- * for itself. This does mean that any parent or ancestor layer that also has the strategy
- * {@link FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's
+ *
+ * The layer itself has the {@link #FRAME_RATE_SELECTION_STRATEGY_PROPAGATE} behavior.
+ * Thus, ancestor layer that also has the strategy
+ * {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's
* frame rate specifications.
* @hide
*/
public static final int FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1;
/**
+ * The layer's frame rate specifications will not propagate to its descendant
+ * layers, even if the descendant layer has no frame rate specifications.
+ * However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor
+ * layer supersedes this behavior.
+ * @hide
+ */
+ public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 2;
+
+ /**
* Builder class for {@link SurfaceControl} objects.
*
* By default the surface will be hidden, and have "unset" bounds, meaning it can
@@ -1576,54 +1587,30 @@ public final class SurfaceControl implements Parcelable {
return mNativeObject != 0;
}
- /*
- * set surface parameters.
- * needs to be inside open/closeTransaction block
- */
-
/** start a transaction
* @hide
- */
- @UnsupportedAppUsage
- public static void openTransaction() {
- synchronized (SurfaceControl.class) {
- if (sGlobalTransaction == null) {
- sGlobalTransaction = new GlobalTransactionWrapper();
- }
- synchronized(SurfaceControl.class) {
- sTransactionNestCount++;
- }
- }
- }
-
- /**
- * Merge the supplied transaction in to the deprecated "global" transaction.
- * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
- * <p>
- * This is a utility for interop with legacy-code and will go away with the Global Transaction.
- * @hide
+ * @deprecated Use regular Transaction instead.
*/
@Deprecated
- public static void mergeToGlobalTransaction(Transaction t) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.merge(t);
- }
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
+ public static void openTransaction() {
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/** end a transaction
* @hide
+ * @deprecated Use regular Transaction instead.
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
public static void closeTransaction() {
- synchronized(SurfaceControl.class) {
- if (sTransactionNestCount == 0) {
- Log.e(TAG,
- "Call to SurfaceControl.closeTransaction without matching openTransaction");
- } else if (--sTransactionNestCount > 0) {
- return;
- }
- sGlobalTransaction.applyGlobalTransaction(false);
- }
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/**
@@ -4499,39 +4486,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * As part of eliminating usage of the global Transaction we expose
- * a SurfaceControl.getGlobalTransaction function. However calling
- * apply on this global transaction (rather than using closeTransaction)
- * would be very dangerous. So for the global transaction we use this
- * subclass of Transaction where the normal apply throws an exception.
- */
- private static class GlobalTransactionWrapper extends SurfaceControl.Transaction {
- void applyGlobalTransaction(boolean sync) {
- applyResizedSurfaces();
- notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync, /*oneWay*/ false);
- }
-
- @Override
- public void apply(boolean sync) {
- throw new RuntimeException("Global transaction must be applied from closeTransaction");
- }
- }
-
- /**
- * This is a refactoring utility function to enable lower levels of code to be refactored
- * from using the global transaction (and instead use a passed in Transaction) without
- * having to refactor the higher levels at the same time.
- * The returned global transaction can't be applied, it must be applied from closeTransaction
- * Unless you are working on removing Global Transaction usage in the WindowManager, this
- * probably isn't a good function to use.
- * @hide
- */
- public static Transaction getGlobalTransaction() {
- return sGlobalTransaction;
- }
-
- /**
* @hide
*/
public void resize(int w, int h) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d591f896d99a..d58c07d96d49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -33018,7 +33018,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
private float getSizePercentage() {
- if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
+ if (mResources == null || getVisibility() != VISIBLE) {
return 0;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7a6c2929c706..cac5387116a1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -991,7 +991,7 @@ public final class ViewRootImpl implements ViewParent,
// for idleness handling.
private boolean mHasIdledMessage = false;
// time for touch boost period.
- private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+ private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
// time for checking idle status periodically.
private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
// time for revaluating the idle status before lowering the frame rate.
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index a11c6d0ce956..a404bd6f8c97 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -54,7 +54,7 @@ oneway interface IWindowMagnificationConnection {
* @param displayId the logical display id.
* @param scale magnification scale.
*/
- void setScale(int displayId, float scale);
+ void setScaleForWindowMagnification(int displayId, float scale);
/**
* Disables window magnification on specified display with animation.
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index e17773159ec5..c6bd20cec07d 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -79,4 +79,9 @@ interface IWebViewUpdateService {
* Used by Settings to enable/disable multiprocess.
*/
void enableMultiProcess(boolean enable);
+
+ /**
+ * Used by Settings to get the default WebView package.
+ */
+ WebViewProviderInfo getDefaultWebViewPackage();
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a919c0079cea..1acebf4df590 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1054,8 +1054,7 @@ public class RemoteViews implements Parcelable, Filter {
}
private class SetRemoteCollectionItemListAdapterAction extends Action {
- @NonNull
- private CompletableFuture<RemoteCollectionItems> mItemsFuture;
+ private @Nullable RemoteCollectionItems mItems;
final Intent mServiceIntent;
int mIntentId = -1;
boolean mIsReplacedIntoAction = false;
@@ -1064,92 +1063,46 @@ public class RemoteViews implements Parcelable, Filter {
@NonNull RemoteCollectionItems items) {
mViewId = id;
items.setHierarchyRootData(getHierarchyRootData());
- mItemsFuture = CompletableFuture.completedFuture(items);
+ mItems = items;
mServiceIntent = null;
}
SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
mViewId = id;
- mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
- setHierarchyRootData(getHierarchyRootData());
+ mItems = null;
mServiceIntent = intent;
}
- private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent) {
- if (intent == null) {
- Log.e(LOG_TAG, "Null intent received when generating adapter future");
- return CompletableFuture.completedFuture(new RemoteCollectionItems
- .Builder().build());
- }
-
- final Context context = ActivityThread.currentApplication();
- final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
-
- context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
- result.defaultExecutor(), new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName,
- IBinder iBinder) {
- RemoteCollectionItems items;
- try {
- items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems();
- } catch (RemoteException re) {
- items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the factory",
- re);
- } finally {
- context.unbindService(this);
- }
-
- result.complete(items);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
- });
-
- result.completeOnTimeout(
- new RemoteCollectionItems.Builder().build(),
- MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
-
- return result;
- }
-
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
mIntentId = parcel.readInt();
- mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
- ? null
- : new RemoteCollectionItems(parcel, getHierarchyRootData()));
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
+ mItems = mServiceIntent != null
+ ? null
+ : new RemoteCollectionItems(parcel, getHierarchyRootData());
}
@Override
public void setHierarchyRootData(HierarchyRootData rootData) {
- if (mIntentId == -1) {
- mItemsFuture = mItemsFuture
- .thenApply(rc -> {
- rc.setHierarchyRootData(rootData);
- return rc;
- });
+ if (mItems != null) {
+ mItems.setHierarchyRootData(rootData);
return;
}
- // Set the root data for items in the cache instead
- mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ if (mIntentId != -1) {
+ // Set the root data for items in the cache instead
+ mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ }
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
dest.writeInt(mIntentId);
- if (mIntentId == -1) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.writeToParcel(dest, flags, /* attached= */ true);
- }
dest.writeTypedObject(mServiceIntent, flags);
+ if (mItems != null) {
+ mItems.writeToParcel(dest, flags, /* attached= */ true);
+ }
}
@Override
@@ -1159,7 +1112,9 @@ public class RemoteViews implements Parcelable, Filter {
if (target == null) return;
RemoteCollectionItems items = mIntentId == -1
- ? getCollectionItemsFromFuture(mItemsFuture)
+ ? mItems == null
+ ? new RemoteCollectionItems.Builder().build()
+ : mItems
: mCollectionCache.getItemsForId(mIntentId);
// Ensure that we are applying to an AppWidget root
@@ -1216,51 +1171,32 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.visitUris(visitor);
- }
- }
+ if (mIntentId != -1 || mItems == null) {
+ return;
+ }
- private static RemoteCollectionItems getCollectionItemsFromFuture(
- CompletableFuture<RemoteCollectionItems> itemsFuture) {
- RemoteCollectionItems items;
- try {
- items = itemsFuture.get();
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error getting collection items from future", e);
- items = new RemoteCollectionItems.Builder().build();
+ mItems.visitUris(visitor);
}
-
- return items;
}
/**
* @hide
*/
- public void collectAllIntents() {
- mCollectionCache.collectAllIntentsNoComplete(this);
+ public CompletableFuture<Void> collectAllIntents() {
+ return mCollectionCache.collectAllIntentsNoComplete(this);
}
private class RemoteCollectionCache {
private SparseArray<String> mIdToUriMapping = new SparseArray<>();
private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
- // We don't put this into the parcel
- private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
- new HashMap<>();
-
RemoteCollectionCache() { }
RemoteCollectionCache(RemoteCollectionCache src) {
- boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
String uri = src.mIdToUriMapping.valueAt(i);
mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
- if (isWaitingCache) {
- mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
- } else {
- mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
- }
+ mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
}
}
@@ -1281,14 +1217,8 @@ public class RemoteViews implements Parcelable, Filter {
void setHierarchyDataForId(int intentId, HierarchyRootData data) {
String uri = mIdToUriMapping.get(intentId);
- if (mTempUriToFutureMapping.get(uri) != null) {
- CompletableFuture<RemoteCollectionItems> itemsFuture =
- mTempUriToFutureMapping.get(uri);
- mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
- rc.setHierarchyRootData(data);
- return rc;
- }));
-
+ if (mUriToCollectionMapping.get(uri) == null) {
+ Log.e(LOG_TAG, "Error setting hierarchy data for id=" + intentId);
return;
}
@@ -1301,14 +1231,17 @@ public class RemoteViews implements Parcelable, Filter {
return mUriToCollectionMapping.get(uri);
}
- void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
if (inViews.hasSizedRemoteViews()) {
for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
- remoteViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(remoteViews));
}
} else if (inViews.hasLandscapeAndPortraitLayouts()) {
- inViews.mLandscape.collectAllIntents();
- inViews.mPortrait.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(
+ collectAllIntentsNoComplete(inViews.mLandscape),
+ collectAllIntentsNoComplete(inViews.mPortrait));
} else if (inViews.mActions != null) {
for (Action action : inViews.mActions) {
if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1318,40 +1251,95 @@ public class RemoteViews implements Parcelable, Filter {
}
if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
- String uri = mIdToUriMapping.get(rca.mIntentId);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ final String uri = mIdToUriMapping.get(rca.mIntentId);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
continue;
}
// Differentiate between the normal collection actions and the ones with
// intents.
if (rca.mServiceIntent != null) {
- String uri = rca.mServiceIntent.toUri(0);
+ final String uri = rca.mServiceIntent.toUri(0);
int index = mIdToUriMapping.indexOfValue(uri);
if (index == -1) {
int newIntentId = mIdToUriMapping.size();
rca.mIntentId = newIntentId;
mIdToUriMapping.put(newIntentId, uri);
- // mUriToIntentMapping.put(uri, mServiceIntent);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
} else {
rca.mIntentId = mIdToUriMapping.keyAt(index);
+ rca.mItems = null;
+ continue;
}
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
} else {
- RemoteCollectionItems items = getCollectionItemsFromFuture(
- rca.mItemsFuture);
- for (RemoteViews views : items.mViews) {
- views.collectAllIntents();
+ for (RemoteViews views : rca.mItems.mViews) {
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(views));
}
}
} else if (action instanceof ViewGroupActionAdd vgaa
&& vgaa.mNestedViews != null) {
- vgaa.mNestedViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(vgaa.mNestedViews));
}
}
}
+
+ return collectionFuture;
+ }
+
+ private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
+ Intent intent) {
+ if (intent == null) {
+ Log.e(LOG_TAG, "Null intent received when generating adapter future");
+ return CompletableFuture.completedFuture(new RemoteCollectionItems
+ .Builder().build());
+ }
+
+ final Context context = ActivityThread.currentApplication();
+ final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
+
+ context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
+ result.defaultExecutor(), new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName,
+ IBinder iBinder) {
+ RemoteCollectionItems items;
+ try {
+ items = IRemoteViewsFactory.Stub.asInterface(iBinder)
+ .getRemoteCollectionItems();
+ } catch (RemoteException re) {
+ items = new RemoteCollectionItems.Builder().build();
+ Log.e(LOG_TAG, "Error getting collection items from the factory",
+ re);
+ } finally {
+ context.unbindService(this);
+ }
+
+ result.complete(items);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+ });
+
+ result.completeOnTimeout(
+ new RemoteCollectionItems.Builder().build(),
+ MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
+
+ return result;
}
public void writeToParcel(Parcel out, int flags) {
@@ -1360,10 +1348,7 @@ public class RemoteViews implements Parcelable, Filter {
out.writeInt(mIdToUriMapping.keyAt(i));
String intentUri = mIdToUriMapping.valueAt(i);
out.writeString8(intentUri);
- RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
- ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
- : mUriToCollectionMapping.get(intentUri);
- items.writeToParcel(out, flags, true);
+ mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
}
}
}
diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
index 5b0d8d1233c6..cc2329fc47cb 100644
--- a/core/java/android/window/SystemPerformanceHinter.java
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -20,7 +20,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
-import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -303,7 +303,7 @@ public class SystemPerformanceHinter {
SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay(
session.displayId);
mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
- FRAME_RATE_SELECTION_STRATEGY_SELF);
+ FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
// smoothSwitchOnly is false to request a higher framerate, even if it means switching
// the display mode will cause would jank on non-VRR devices because keeping a lower
// refresh rate would mean a poorer user experience.
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 4e0f9a51c0a0..0ec9ffe6390b 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -108,6 +108,18 @@ public final class TaskFragmentOperation implements Parcelable {
*/
public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
+ /**
+ * Creates a decor surface in the parent Task of the TaskFragment. The created decor surface
+ * will be provided in {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED}
+ * event callback.
+ */
+ public static final int OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE = 14;
+
+ /**
+ * Removes the decor surface in the parent Task of the TaskFragment.
+ */
+ public static final int OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE = 15;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -124,6 +136,8 @@ public final class TaskFragmentOperation implements Parcelable {
OP_TYPE_SET_ISOLATED_NAVIGATION,
OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
OP_TYPE_REORDER_TO_TOP_OF_TASK,
+ OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE,
+ OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index e6eeca4b4801..a77c23475c60 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -22,6 +22,9 @@ import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+import java.util.Objects;
/**
* The information about the parent Task of a particular TaskFragment
@@ -37,12 +40,15 @@ public class TaskFragmentParentInfo implements Parcelable {
private final boolean mHasDirectActivity;
+ @Nullable private final SurfaceControl mDecorSurface;
+
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
- boolean visible, boolean hasDirectActivity) {
+ boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
mVisible = visible;
mHasDirectActivity = hasDirectActivity;
+ mDecorSurface = decorSurface;
}
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
@@ -50,6 +56,7 @@ public class TaskFragmentParentInfo implements Parcelable {
mDisplayId = info.mDisplayId;
mVisible = info.mVisible;
mHasDirectActivity = info.mHasDirectActivity;
+ mDecorSurface = info.mDecorSurface;
}
/** The {@link Configuration} of the parent Task */
@@ -92,7 +99,13 @@ public class TaskFragmentParentInfo implements Parcelable {
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
- && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity;
+ && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+ && mDecorSurface == that.mDecorSurface;
+ }
+
+ @Nullable
+ public SurfaceControl getDecorSurface() {
+ return mDecorSurface;
}
@WindowConfiguration.WindowingMode
@@ -107,6 +120,7 @@ public class TaskFragmentParentInfo implements Parcelable {
+ ", displayId=" + mDisplayId
+ ", visible=" + mVisible
+ ", hasDirectActivity=" + mHasDirectActivity
+ + ", decorSurface=" + mDecorSurface
+ "}";
}
@@ -128,7 +142,8 @@ public class TaskFragmentParentInfo implements Parcelable {
return mConfiguration.equals(that.mConfiguration)
&& mDisplayId == that.mDisplayId
&& mVisible == that.mVisible
- && mHasDirectActivity == that.mHasDirectActivity;
+ && mHasDirectActivity == that.mHasDirectActivity
+ && mDecorSurface == that.mDecorSurface;
}
@Override
@@ -137,6 +152,7 @@ public class TaskFragmentParentInfo implements Parcelable {
result = 31 * result + mDisplayId;
result = 31 * result + (mVisible ? 1 : 0);
result = 31 * result + (mHasDirectActivity ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mDecorSurface);
return result;
}
@@ -146,6 +162,7 @@ public class TaskFragmentParentInfo implements Parcelable {
dest.writeInt(mDisplayId);
dest.writeBoolean(mVisible);
dest.writeBoolean(mHasDirectActivity);
+ dest.writeTypedObject(mDecorSurface, flags);
}
private TaskFragmentParentInfo(Parcel in) {
@@ -153,6 +170,7 @@ public class TaskFragmentParentInfo implements Parcelable {
mDisplayId = in.readInt();
mVisible = in.readBoolean();
mHasDirectActivity = in.readBoolean();
+ mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
}
public static final Creator<TaskFragmentParentInfo> CREATOR =
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 35ce72620d09..34c639974bfd 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -19,6 +19,7 @@ package android.window;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -87,6 +88,38 @@ public class WindowInfosListenerForTest {
@NonNull
public final Matrix transform;
+ /**
+ * True if the window is touchable.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isTouchable;
+
+ /**
+ * True if the window is focusable.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isFocusable;
+
+ /**
+ * True if the window is preventing splitting
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isPreventSplitting;
+
+ /**
+ * True if the window duplicates touches received to wallpaper.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isDuplicateTouchToWallpaper;
+
+ /**
+ * True if the window is listening for when there is a touch DOWN event
+ * occurring outside its touchable bounds. When such an event occurs,
+ * this window will receive a MotionEvent with ACTION_OUTSIDE.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isWatchOutsideTouch;
+
WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
@NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
this.windowToken = windowToken;
@@ -96,6 +129,14 @@ public class WindowInfosListenerForTest {
this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
this.transform = transform;
+ this.isTouchable = (inputConfig & InputConfig.NOT_TOUCHABLE) == 0;
+ this.isFocusable = (inputConfig & InputConfig.NOT_FOCUSABLE) == 0;
+ this.isPreventSplitting = (inputConfig
+ & InputConfig.PREVENT_SPLITTING) != 0;
+ this.isDuplicateTouchToWallpaper = (inputConfig
+ & InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER) != 0;
+ this.isWatchOutsideTouch = (inputConfig
+ & InputConfig.WATCH_OUTSIDE_TOUCH) != 0;
}
@Override
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 7f93213bf884..29932f342b74 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -48,3 +48,11 @@ flag {
is_fixed_read_only: true
bug: "262477923"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "secure_window_state"
+ description: "Move SC secure flag to WindowState level"
+ is_fixed_read_only: true
+ bug: "308662081"
+}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7c4252e7aa5d..6b074a610818 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -33,6 +33,7 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
@@ -131,6 +132,18 @@ public final class AccessibilityStatsLogUtils {
}
/**
+ * Logs magnification that is assigned to the two finger triple tap shortcut. Calls this when
+ * triggering the magnification two finger triple tap shortcut.
+ */
+ public static void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED,
+ MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+ // jean update
+ ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP,
+ convertToLoggingServiceStatus(enabled));
+ }
+
+ /**
* Logs accessibility feature name that is assigned to the long pressed accessibility button
* shortcut. Calls this when clicking the long pressed accessibility button shortcut.
*
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
index f5fe12eb66c0..e55c64199f45 100644
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -37,22 +37,11 @@ public class RefreshRateSettingsUtils {
* @return The highest refresh rate
*/
public static float findHighestRefreshRateForDefaultDisplay(Context context) {
- return findHighestRefreshRate(context, Display.DEFAULT_DISPLAY);
- }
-
- /**
- * Find the highest refresh rate among all the modes of the specified display.
- *
- * @param context The context
- * @param displayId The display ID
- * @return The highest refresh rate
- */
- public static float findHighestRefreshRate(Context context, int displayId) {
final DisplayManager dm = context.getSystemService(DisplayManager.class);
- final Display display = dm.getDisplay(displayId);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
if (display == null) {
- Log.w(TAG, "No valid display device with ID = " + displayId);
+ Log.w(TAG, "No valid default display device");
return DEFAULT_REFRESH_RATE;
}
diff --git a/core/java/com/android/internal/os/MonotonicClock.java b/core/java/com/android/internal/os/MonotonicClock.java
index d0d2354e7007..661628a8c148 100644
--- a/core/java/com/android/internal/os/MonotonicClock.java
+++ b/core/java/com/android/internal/os/MonotonicClock.java
@@ -50,6 +50,8 @@ public class MonotonicClock {
private final Clock mClock;
private long mTimeshift;
+ public static final long UNDEFINED = -1;
+
public MonotonicClock(File file) {
mFile = new AtomicFile(file);
mClock = Clock.SYSTEM_CLOCK;
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index dadeb2b74c7d..aab22421b334 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -60,7 +60,8 @@ interface ITelephonyRegistry {
@UnsupportedAppUsage(maxTargetSdk = 28)
void notifyCallForwardingChanged(boolean cfi);
void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
- void notifyDataActivityForSubscriber(int phoneId, int subId, int state);
+ void notifyDataActivityForSubscriber(int subId, int state);
+ void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state);
void notifyDataConnectionForSubscriber(
int phoneId, int subId, in PreciseDataConnectionState preciseState);
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 440a33244a62..f365dbb1d46a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -462,9 +462,4 @@ cc_library_shared_for_libandroid_runtime {
],
},
},
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 262f5e8e761e..239c6260800b 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -81,7 +81,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
deviceInfo.getId(), deviceInfo.getGeneration(),
deviceInfo.getControllerNumber(), nameObj.get(),
static_cast<int32_t>(ident.vendor),
- static_cast<int32_t>(ident.product), descriptorObj.get(),
+ static_cast<int32_t>(ident.product),
+ static_cast<int32_t>(ident.bus), descriptorObj.get(),
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
keyboardLanguageTagObj.get(), keyboardLayoutTypeObj.get(),
@@ -111,7 +112,7 @@ int register_android_view_InputDevice(JNIEnv* env)
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/"
+ "(IIILjava/lang/String;IIILjava/lang/"
"String;ZIILandroid/view/KeyCharacterMap;Ljava/"
"lang/String;Ljava/lang/String;ZZZZZIII)V");
diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index 2b74220a1471..11b367b51ae2 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -92,8 +92,24 @@ message BatteryUsageStatsAtomsProto {
message UidBatteryConsumer {
optional int32 uid = 1;
optional BatteryConsumerData battery_consumer_data = 2;
- optional int64 time_in_foreground_millis = 3;
- optional int64 time_in_background_millis = 4;
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_foreground_millis = 3 [deprecated = true];
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_background_millis = 4 [deprecated = true];
+
+ message TimeInState {
+ enum ProcessState {
+ UNSPECIFIED = 0;
+ FOREGROUND = 1;
+ BACKGROUND = 2;
+ FOREGROUND_SERVICE = 3;
+ }
+
+ optional ProcessState process_state = 1;
+ optional int64 time_in_state_millis = 2;
+ }
+
+ repeated TimeInState time_in_state = 5;
}
repeated UidBatteryConsumer uid_battery_consumers = 5;
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index d5cf60d94e3a..c242c9ca3218 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -110,6 +110,7 @@ message EventObfuscatedProto {
optional int32 task_root_package_token = 11;
optional int32 task_root_class_token = 12;
optional int32 locus_id_token = 13;
+ optional ObfuscatedUserInteractionExtrasProto interaction_extras = 14;
}
/**
@@ -129,6 +130,7 @@ message PendingEventProto {
optional string task_root_package = 11;
optional string task_root_class = 12;
optional string locus_id = 13 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional bytes extras = 14;
}
/**
@@ -145,3 +147,11 @@ message ObfuscatedPackagesProto {
// Stores the mappings for every package
repeated PackagesMap packages_map = 2;
}
+
+/**
+ * Store the relevant information from extra details for user interaction event.
+ */
+message ObfuscatedUserInteractionExtrasProto {
+ optional int32 category_token = 1;
+ optional int32 action_token = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d208c6ed30a..002164011e84 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7817,6 +7817,13 @@
<permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an app to track all preparations for a complete factory reset.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.PREPARE_FACTORY_RESET"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index f24c3f59155a..332ad2a82d38 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -47,6 +47,10 @@ per-file res/values/config_device_idle.xml = file:/apex/jobscheduler/OWNERS
# Wear
per-file res/*-watch/* = file:/WEAR_OWNERS
+# Peformance
+per-file res/values/config.xml = file:/PERFORMANCE_OWNERS
+per-file res/values/symbols.xml = file:/PERFORMANCE_OWNERS
+
# PowerProfile
per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS
per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6cd6eb4b8df9..1d4e01a95591 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1522,6 +1522,10 @@
config_screenBrightnessSettingDefaultFloat instead -->
<integer name="config_screenBrightnessSettingDefault">102</integer>
+ <!-- Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ The value must be in the range [0, 255]. -->
+ <integer name="config_screenBrightnessCapForWearBedtimeMode">20</integer>
+
<!-- Minimum screen brightness setting allowed by power manager.
-2 is invalid so setting will resort to int value specified above.
Set this to 0.0 to allow screen to go to minimal brightness.
@@ -4336,6 +4340,9 @@
<!-- True if assistant app should be pinned via Pinner Service -->
<bool name="config_pinnerAssistantApp">false</bool>
+ <!-- Bytes that the PinnerService will pin for WebView -->
+ <integer name="config_pinnerWebviewPinBytes">0</integer>
+
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
@@ -4986,6 +4993,11 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
+ <!-- Packages that contain a security state.
+ {@link SecurityStateManager#getGlobalSecurityState} will read and report the state/version
+ of these packages. -->
+ <string-array name="config_securityStatePackages" translatable="false" />
+
<!-- Package name for the default Health Connect app.
OEMs can set this with their own health app package name to define a default app with high
priority for the app to store the health data. If set the app always has priority of 1
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 38f1f6756d17..4b0fa4ba5173 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1274,6 +1274,7 @@
<java-symbol type="array" name="policy_exempt_apps" />
<java-symbol type="array" name="vendor_policy_exempt_apps" />
<java-symbol type="array" name="cloneable_apps" />
+ <java-symbol type="array" name="config_securityStatePackages" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -2078,6 +2079,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
+ <java-symbol type="integer" name="config_screenBrightnessCapForWearBedtimeMode" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
@@ -3385,6 +3387,7 @@
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="bool" name="config_pinnerAssistantApp" />
+ <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index aaaa3c7740c5..ac1f7d0e345f 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -48,6 +48,11 @@ public class BatteryUsageStatsPulledTest {
private static final int UID_1 = 2000;
private static final int UID_2 = 3000;
private static final int UID_3 = 4000;
+ private static final int[] UID_USAGE_TIME_PROCESS_STATES = {
+ BatteryConsumer.PROCESS_STATE_FOREGROUND,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE
+ };
@Test
public void testGetStatsProto() {
@@ -195,6 +200,20 @@ public class BatteryUsageStatsPulledTest {
assertEquals("For uid " + uid,
uidConsumer.getTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND),
uidConsumerProto.timeInBackgroundMillis);
+ for (int processState : UID_USAGE_TIME_PROCESS_STATES) {
+ final long timeInStateMillis = uidConsumer.getTimeInProcessStateMs(processState);
+ if (timeInStateMillis <= 0) {
+ continue;
+ }
+ assertEquals("For uid " + uid + ", process state " + processState,
+ timeInStateMillis,
+ Arrays.stream(uidConsumerProto.timeInState)
+ .filter(timeInState -> timeInState.processState == processState)
+ .findFirst()
+ .orElseThrow()
+ .timeInStateMillis);
+ }
+
if (expectNullBatteryConsumerData) {
assertNull("For uid " + uid, uidConsumerProto.batteryConsumerData);
} else {
@@ -250,8 +269,8 @@ public class BatteryUsageStatsPulledTest {
final UidBatteryConsumer.Builder uidBuilder = builder
.getOrCreateUidBatteryConsumerBuilder(UID_0)
.setPackageWithHighestDrain("myPackage0")
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 2000)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(
@@ -285,7 +304,7 @@ public class BatteryUsageStatsPulledTest {
builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
.setPackageWithHighestDrain("myPackage1")
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234);
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
builder.getOrCreateUidBatteryConsumerBuilder(UID_2)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
@@ -331,8 +350,10 @@ public class BatteryUsageStatsPulledTest {
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
builder.getOrCreateUidBatteryConsumerBuilder(i)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2 * 60 * 1000)
+ .setTimeInProcessStateMs(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 1 * 60 * 1000)
+ .setTimeInProcessStateMs(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 2 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 30)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 40);
}
@@ -340,16 +361,16 @@ public class BatteryUsageStatsPulledTest {
// Add a UID with much larger battery footprint
final int largeConsumerUid = 3001;
builder.getOrCreateUidBatteryConsumerBuilder(largeConsumerUid)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 10 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 20 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 10 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 20 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
// Add a UID with much larger usage duration
final int highUsageUid = 3002;
builder.getOrCreateUidBatteryConsumerBuilder(highUsageUid)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 60 * 60 * 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 120 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 60 * 60 * 1000)
+ .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_BACKGROUND, 120 * 60 * 1000)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, 3)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 4);
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 5a202c5e8834..9dce899aa92d 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import android.app.usage.UsageEvents.Event;
import android.content.res.Configuration;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -140,6 +141,12 @@ public class ParcelableUsageEventListTest {
case Event.LOCUS_ID_SET:
event.mLocusId = anyString();
break;
+ case Event.USER_INTERACTION:
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, anyString());
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, anyString());
+ event.mExtras = extras;
+ break;
}
event.mFlags = anyInt();
@@ -176,6 +183,14 @@ public class ParcelableUsageEventListTest {
case Event.LOCUS_ID_SET:
assertEquals(ue1.mLocusId, ue2.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ final PersistableBundle extras1 = ue1.getExtras();
+ final PersistableBundle extras2 = ue2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY));
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION));
+ break;
}
assertEquals(ue1.mFlags, ue2.mFlags);
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 083e37a3aaa6..fae714842b9b 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -72,7 +72,7 @@ public class UsageStatsPersistenceTest {
"mShortcutId", "mShortcutIdToken", "mBucketAndReason", "mInstanceId",
"mNotificationChannelId", "mNotificationChannelIdToken", "mTaskRootPackage",
"mTaskRootPackageToken", "mTaskRootClass", "mTaskRootClassToken", "mLocusId",
- "mLocusIdToken"};
+ "mLocusIdToken", "mExtras", "mUserInteractionExtrasToken"};
// All fields in this list are defined in UsageEvents.Event but not persisted
private static final String[] USAGEEVENTS_IGNORED_FIELDS = {"mAction", "mContentAnnotations",
"mContentType", "DEVICE_EVENT_PACKAGE_NAME", "FLAG_IS_PACKAGE_INSTANT_APP",
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 87c167c0df07..4a9cb7180a3f 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -83,6 +83,12 @@ public class ResourcesManagerTest extends TestCase {
}
@Override
+ protected AssetManager createAssetManager(@NonNull final ResourcesKey key,
+ ResourcesManager.ApkAssetsSupplier apkSupplier) {
+ return createAssetManager(key);
+ }
+
+ @Override
protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
return mDisplayMetricsMap.get(displayId);
}
@@ -100,7 +106,7 @@ public class ResourcesManagerTest extends TestCase {
null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
- assertSame(resources, newResources);
+ assertSame(resources.getImpl(), newResources.getImpl());
}
@SmallTest
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 0855268411eb..1bdb006c3465 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -472,6 +472,9 @@ public class NotificationRankingUpdateTest {
NotificationRankingUpdate nru = generateUpdate(getContext());
Parcel parcel = Parcel.obtain();
nru.writeToParcel(parcel, 0);
+ if (Flags.rankingUpdateAshmem()) {
+ assertTrue(nru.isFdNotNullAndClosed());
+ }
parcel.setDataPosition(0);
NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
// The rankingUpdate file descriptor is only non-null in the new path.
diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
index 6229530dc33f..3147eac30705 100644
--- a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -21,7 +21,7 @@ import static android.os.PerformanceHintManager.Session.CPU_LOAD_UP;
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
-import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE;
import static android.window.SystemPerformanceHinter.HINT_ADPF;
import static android.window.SystemPerformanceHinter.HINT_ALL;
import static android.window.SystemPerformanceHinter.HINT_SF_EARLY_WAKEUP;
@@ -170,7 +170,7 @@ public class SystemPerformanceHinterTests {
// Verify we call SF
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -262,7 +262,7 @@ public class SystemPerformanceHinterTests {
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -283,7 +283,7 @@ public class SystemPerformanceHinterTests {
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -334,7 +334,7 @@ public class SystemPerformanceHinterTests {
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -385,7 +385,7 @@ public class SystemPerformanceHinterTests {
session1.close();
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -410,7 +410,7 @@ public class SystemPerformanceHinterTests {
anyInt());
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mSecondaryDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f19acbe023b8..2237ba1924db 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -529,6 +529,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1582845629": {
+ "message": "Starting animation on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1575977269": {
"message": "Skipping %s: mismatch root %s",
"level": "DEBUG",
@@ -925,6 +931,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-1243510456": {
+ "message": "Dim animation requested: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1237827119": {
"message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s",
"level": "VERBOSE",
@@ -991,12 +1003,6 @@
"group": "WM_DEBUG_WINDOW_INSETS",
"at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
},
- "-1176488860": {
- "message": "SURFACE isSecure=%b: %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"-1164930508": {
"message": "Moving to RESUMED: %s (starting new instance) callers=%s",
"level": "VERBOSE",
@@ -1177,6 +1183,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
+ "-1028213464": {
+ "message": "%s skipping animation and directly setting alpha=%f, blur=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1022146708": {
"message": "Skipping %s: mismatch activity type",
"level": "DEBUG",
@@ -1801,12 +1813,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-504637678": {
- "message": "Starting animation on dim layer %s, requested by %s, alpha: %f -> %f, blur: %d -> %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"-503656156": {
"message": "Update process config of %s to new config %s",
"level": "VERBOSE",
@@ -3277,6 +3283,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "810599500": {
+ "message": "SURFACE isSecure=%b: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"829434921": {
"message": "Draw state now committed in %s",
"level": "VERBOSE",
@@ -4027,12 +4039,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1620751818": {
- "message": "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"1621562070": {
"message": " startWCT=%s",
"level": "VERBOSE",
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bcee1a0e..dd703f5eefb9 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@ import android.system.keystore2.ResponseCode;
import android.util.Log;
import java.util.Calendar;
-import java.util.Objects;
/**
* @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@ public class KeyStore2 {
return new KeyStore2();
}
+ /**
+ * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+ *
+ * @throws IllegalStateException if the KeystoreService is not available or has not
+ * been initialized when called. This is a state that should not happen and indicates
+ * and error somewhere in the stack or with the calling processes access permissions.
+ */
@NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
- .getService(KEYSTORE2_SERVICE_NAME));
- Binder.allowBlocking(mBinder.asBinder());
+ .getService(KEYSTORE2_SERVICE_NAME));
+ }
+ if (mBinder == null) {
+ throw new IllegalStateException(
+ "Could not connect to Keystore service. Keystore may have crashed or not been"
+ + " initialized");
}
- return Objects.requireNonNull(mBinder);
+ Binder.allowBlocking(mBinder.asBinder());
+ return mBinder;
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 50cfd941adb3..4c2433fab2f8 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -443,7 +443,8 @@ public class OverlayPresentationTest {
assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 02031a67e7e3..8c274a26177d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1139,7 +1139,8 @@ public class SplitControllerTest {
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */);
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(TASK_ID)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index e56c8ab686e7..7b77235f66f7 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -79,14 +79,16 @@ public class TaskContainerTest {
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -106,13 +108,15 @@ public class TaskContainerTest {
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertFalse(taskContainer.isInPictureInPicture());
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+ null /* decorSurface */));
assertTrue(taskContainer.isInPictureInPicture());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index f5b877a70b84..a3eb429b1d7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -412,6 +412,23 @@ public class BubbleExpandedView extends LinearLayout {
setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
}
+
+ /** Updates the width of the task view if it changed. */
+ void updateTaskViewContentWidth() {
+ if (mTaskView != null) {
+ int width = getContentWidth();
+ if (mTaskView.getWidth() != width) {
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width, MATCH_PARENT);
+ mTaskView.setLayoutParams(lp);
+ }
+ }
+ }
+
+ private int getContentWidth() {
+ boolean isStackOnLeft = mPositioner.isStackOnLeft(mStackView.getStackPosition());
+ return mPositioner.getTaskViewContentWidth(isStackOnLeft);
+ }
+
/**
* Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
* to be called after view inflate.
@@ -438,7 +455,12 @@ public class BubbleExpandedView extends LinearLayout {
mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
mTaskView = new TaskView(mContext, mTaskViewTaskController);
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
- mExpandedViewContainer.addView(mTaskView);
+
+ // set a fixed width so it is not recalculated as part of a rotation. the width will be
+ // updated manually after the rotation.
+ FrameLayout.LayoutParams lp =
+ new FrameLayout.LayoutParams(getContentWidth(), MATCH_PARENT);
+ mExpandedViewContainer.addView(mTaskView, lp);
bringChildToFront(mTaskView);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 1efd9df3a1d9..baa52a0b5626 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -375,6 +375,13 @@ public class BubblePositioner {
}
}
+ /** Returns the width of the task view content. */
+ public int getTaskViewContentWidth(boolean onLeft) {
+ int[] paddings = getExpandedViewContainerPadding(onLeft, /* isOverflow = */ false);
+ int pointerOffset = showBubblesVertically() ? getPointerSize() : 0;
+ return mPositionRect.width() - paddings[0] - paddings[2] - pointerOffset;
+ }
+
/** Gets the y position of the expanded view if it was top-aligned. */
public float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8f904c42d247..91a8ce726c42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -537,8 +537,8 @@ public class BubbleStackView extends FrameLayout
return;
}
- final boolean clickedBubbleIsCurrentlyExpandedBubble =
- clickedBubble.getKey().equals(mExpandedBubble.getKey());
+ final boolean clickedBubbleIsCurrentlyExpandedBubble = mExpandedBubble != null
+ && clickedBubble.getKey().equals(mExpandedBubble.getKey());
if (isExpanded()) {
mExpandedAnimationController.onGestureFinished();
@@ -3288,6 +3288,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x));
mExpandedViewContainer.setTranslationX(0f);
+ mExpandedBubble.getExpandedView().updateTaskViewContentWidth();
mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen());
updatePointerPosition(false /* forIme */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 271a3b26305d..63afd3e7a2ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -627,7 +627,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
&& mRecentsTask.equals(change.getContainer());
hasTaskChange = hasTaskChange || isRootTask;
final boolean isLeafTask = leafTaskFilter.test(change);
- if (TransitionUtil.isOpeningType(change.getMode())) {
+ if (TransitionUtil.isOpeningType(change.getMode())
+ || TransitionUtil.isOrderOnly(change)) {
if (isRecentsTask) {
recentsOpening = change;
} else if (isRootTask || isLeafTask) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index ef8393c3b5b1..35a1fa0a92f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -151,7 +151,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
- runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ if (mHandler.getLooper().isCurrentThread()) {
+ // We can only use the transaction if it can updated synchronously, otherwise the tx
+ // will be applied immediately after but also used/updated on the view thread which
+ // will lead to a race and/or crash
+ runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ } else {
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8cbcde320795..53ec20192f2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -49,7 +49,6 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
-import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
@@ -70,7 +69,9 @@ class DragResizeInputListener implements AutoCloseable {
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
private final int mDisplayId;
- private final BaseIWindow mFakeWindow;
+
+ private final IBinder mClientToken;
+
private final IBinder mFocusGrantToken;
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
@@ -78,7 +79,7 @@ class DragResizeInputListener implements AutoCloseable {
private final DragPositioningCallback mCallback;
private final SurfaceControl mInputSinkSurface;
- private final BaseIWindow mFakeSinkWindow;
+ private final IBinder mSinkClientToken;
private final InputChannel mSinkInputChannel;
private final DisplayController mDisplayController;
@@ -116,17 +117,14 @@ class DragResizeInputListener implements AutoCloseable {
mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
mDisplayController = displayController;
- // Use a fake window as the backing surface is a container layer, and we don't want to
- // create a buffer layer for it, so we can't use ViewRootImpl.
- mFakeWindow = new BaseIWindow();
- mFakeWindow.setSession(mWindowSession);
+ mClientToken = new Binder();
mFocusGrantToken = new Binder();
mInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mDecorationSurface,
- mFakeWindow.asBinder(),
+ mClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
@@ -155,13 +153,13 @@ class DragResizeInputListener implements AutoCloseable {
.setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
.show(mInputSinkSurface)
.apply();
- mFakeSinkWindow = new BaseIWindow();
+ mSinkClientToken = new Binder();
mSinkInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mInputSinkSurface,
- mFakeSinkWindow.asBinder(),
+ mSinkClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
0 /* privateFlags */,
@@ -324,14 +322,14 @@ class DragResizeInputListener implements AutoCloseable {
mInputEventReceiver.dispose();
mInputChannel.dispose();
try {
- mWindowSession.remove(mFakeWindow.asBinder());
+ mWindowSession.remove(mClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
mSinkInputChannel.dispose();
try {
- mWindowSession.remove(mFakeSinkWindow.asBinder());
+ mWindowSession.remove(mSinkClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 386983ce6aae..b9b56c2ae950 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -132,5 +132,6 @@ android_test {
csuite_test {
name: "csuite-1p3p-pip-flickers",
+ test_plan_include: "csuitePlan.xml",
test_config_template: "csuiteDefaultTemplate.xml",
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 6429b00a2a58..f5a8655b81f0 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -45,12 +45,15 @@
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="run-command" value="settings put global package_verifier_user_consent -1"/>
<option name="teardown-command"
value="settings delete secure show_ime_with_hard_keyboard"/>
<option name="teardown-command" value="settings delete system show_touches"/>
<option name="teardown-command" value="settings delete system pointer_location"/>
<option name="teardown-command"
value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ <option name="teardown-command"
+ value="settings put global package_verifier_user_consent 1"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml
new file mode 100644
index 000000000000..a2fc6b45c2ad
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuitePlan.xml
@@ -0,0 +1,3 @@
+<configuration description="Flicker tests C-Suite Crawler Test Plan">
+ <target_preparer class="com.android.csuite.core.AppCrawlTesterHostPreparer"/>
+</configuration> \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
index 45dd38d10ef5..b91d6f90ed8e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
@@ -13,18 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.filters
+
+package com.android.wm.shell;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
/**
- * An [OutputFilter] that keeps all classes by default. (but none of its members)
- *
- * We're not currently using it, but using it *might* make certain things easier. For example, with
- * this, all classes would at least be loadable.
+ * Basic test handler that immediately executes anything that is posted on it.
*/
-class KeepAllClassesFilter(fallback: OutputFilter) : DelegatingFilter(fallback) {
- override fun getPolicyForClass(className: String): FilterPolicyWithReason {
- // If the default visibility wouldn't keep it, change it to "keep".
- val f = super.getPolicyForClass(className)
- return f.promoteToKeep("keep-all-classes")
+public class TestHandler extends Handler {
+ public TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ dispatchMessage(msg);
+ return true;
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 4afb29ecd98c..d7c46104b6b1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -40,6 +40,7 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -58,6 +59,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestHandler;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
@@ -92,8 +94,7 @@ public class TaskViewTest extends ShellTestCase {
Transitions mTransitions;
@Mock
Looper mViewLooper;
- @Mock
- Handler mViewHandler;
+ TestHandler mViewHandler;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -112,7 +113,7 @@ public class TaskViewTest extends ShellTestCase {
mContext = getContext();
doReturn(true).when(mViewLooper).isCurrentThread();
- doReturn(mViewLooper).when(mViewHandler).getLooper();
+ mViewHandler = spy(new TestHandler(mViewLooper));
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
@@ -668,4 +669,24 @@ public class TaskViewTest extends ShellTestCase {
mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
verify(mViewHandler).post(any());
}
+
+ @Test
+ public void testSetResizeBgOnSameUiThread_expectUsesTransaction() {
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler, never()).post(any());
+ verify(mTaskView, never()).setResizeBackgroundColor(eq(Color.BLUE));
+ verify(mTaskView).setResizeBackgroundColor(eq(tx), eq(Color.BLUE));
+ }
+
+ @Test
+ public void testSetResizeBgOnDifferentUiThread_expectDoesNotUseTransaction() {
+ doReturn(false).when(mViewLooper).isCurrentThread();
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler).post(any());
+ verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
+ }
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index a1d6ab5798e1..b1cf96d41497 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -113,13 +113,13 @@ package android.location {
}
public final class GnssMeasurementRequest implements android.os.Parcelable {
- method @NonNull public android.os.WorkSource getWorkSource();
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull public android.os.WorkSource getWorkSource();
method public boolean isCorrelationVectorOutputsEnabled();
}
public static final class GnssMeasurementRequest.Builder {
method @NonNull public android.location.GnssMeasurementRequest.Builder setCorrelationVectorOutputsEnabled(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
public final class GnssReflectingPlane implements android.os.Parcelable {
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index 65af3928bd02..2f0835ab8af5 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -17,11 +17,13 @@
package android.location;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
@@ -121,6 +123,7 @@ public final class GnssMeasurementRequest implements Parcelable {
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
public @NonNull WorkSource getWorkSource() {
return mWorkSource;
@@ -298,6 +301,7 @@ public final class GnssMeasurementRequest implements Parcelable {
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
@RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index c471a2749617..b6055e818f8c 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -5,4 +5,18 @@ flag {
namespace: "location"
description: "Flag for GNSS API for NavIC L1"
bug: "302199306"
-} \ No newline at end of file
+}
+
+flag {
+ name: "gnss_call_stop_before_set_position_mode"
+ namespace: "location"
+ description: "Flag for calling stop() before setPositionMode()"
+ bug: "306874828"
+}
+
+flag {
+ name: "gnss_api_measurement_request_work_source"
+ namespace: "location"
+ description: "Flag for GnssMeasurementRequest WorkSource API"
+ bug: "295235160"
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d14775fa9ad9..5c8758a6aa73 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -739,7 +739,7 @@ interface IAudioService {
oneway void stopLoudnessCodecUpdates(int piid);
- oneway void addLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
+ oneway void addLoudnessCodecInfo(int piid, int mediaCodecHash, in LoudnessCodecInfo codecInfo);
oneway void removeLoudnessCodecInfo(int piid, in LoudnessCodecInfo codecInfo);
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index 92f337244daf..de9d87c0b28c 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -30,10 +30,10 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -47,9 +47,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
* parameter updates are defined by the CTA-2075 standard.
* <p>A new object should be instantiated for each {@link AudioTrack} with the help
* of {@link #create()} or {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public class LoudnessCodecConfigurator {
@@ -57,9 +54,6 @@ public class LoudnessCodecConfigurator {
/**
* Listener used for receiving asynchronous loudness metadata updates.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public interface OnLoudnessCodecUpdateListener {
@@ -76,9 +70,6 @@ public class LoudnessCodecConfigurator {
* @return a Bundle which contains the original computed codecValues
* aggregated with user edits. The platform will configure the associated
* MediaCodecs with the returned Bundle params.
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
@NonNull
@@ -112,9 +103,6 @@ public class LoudnessCodecConfigurator {
* Otherwise, use {@link #create(Executor, OnLoudnessCodecUpdateListener)}.
*
* @return the {@link LoudnessCodecConfigurator} instance
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public static @NonNull LoudnessCodecConfigurator create() {
@@ -133,9 +121,6 @@ public class LoudnessCodecConfigurator {
* @param listener used for receiving updates
*
* @return the {@link LoudnessCodecConfigurator} instance
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public static @NonNull LoudnessCodecConfigurator create(
@@ -200,12 +185,9 @@ public class LoudnessCodecConfigurator {
* method will have the effect of clearing the existing set
* {@link AudioTrack} and will stop receiving asynchronous
* loudness updates
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
- public void setAudioTrack(AudioTrack audioTrack) {
+ public void setAudioTrack(@Nullable AudioTrack audioTrack) {
List<LoudnessCodecInfo> codecInfos;
int piid = PLAYER_PIID_INVALID;
int oldPiid = PLAYER_PIID_INVALID;
@@ -250,10 +232,11 @@ public class LoudnessCodecConfigurator {
* previously added.
*
* @param mediaCodec the codec to start receiving asynchronous loudness
- * updates
- *
- * TODO: remove hide once API is final
- * @hide
+ * updates. The codec has to be in a configured or started
+ * state in order to add it for loudness updates.
+ * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+ * does not contain loudness metadata or if it
+ * was already added before
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
@@ -262,31 +245,32 @@ public class LoudnessCodecConfigurator {
int piid = PLAYER_PIID_INVALID;
final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
- if (mcInfo != null) {
- synchronized (mConfiguratorLock) {
- final AtomicBoolean containsCodec = new AtomicBoolean(false);
- Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
- containsCodec.set(!codecSet.add(mc));
- return codecSet;
- });
- if (newSet == null) {
- newSet = new HashSet<>();
- newSet.add(mc);
- mMediaCodecs.put(mcInfo, newSet);
- }
- if (containsCodec.get()) {
- Log.v(TAG, "Loudness configurator already added media codec " + mediaCodec);
- return;
- }
- if (mAudioTrack != null) {
- piid = mAudioTrack.getPlayerIId();
- }
+ if (mcInfo == null) {
+ throw new IllegalArgumentException("Could not extract codec loudness information");
+ }
+ synchronized (mConfiguratorLock) {
+ final AtomicBoolean containsCodec = new AtomicBoolean(false);
+ Set<MediaCodec> newSet = mMediaCodecs.computeIfPresent(mcInfo, (info, codecSet) -> {
+ containsCodec.set(!codecSet.add(mc));
+ return codecSet;
+ });
+ if (newSet == null) {
+ newSet = new HashSet<>();
+ newSet.add(mc);
+ mMediaCodecs.put(mcInfo, newSet);
}
-
- if (piid != PLAYER_PIID_INVALID) {
- mLcDispatcher.addLoudnessCodecInfo(piid, mcInfo);
+ if (containsCodec.get()) {
+ throw new IllegalArgumentException(
+ "Loudness configurator already added " + mediaCodec);
+ }
+ if (mAudioTrack != null) {
+ piid = mAudioTrack.getPlayerIId();
}
}
+
+ if (piid != PLAYER_PIID_INVALID) {
+ mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
+ }
}
/**
@@ -297,37 +281,44 @@ public class LoudnessCodecConfigurator {
* <p>No elements will be removed if the passed mediaCodec was not added before.
*
* @param mediaCodec the element to remove for receiving asynchronous updates
- *
- * TODO: remove hide once API is final
- * @hide
+ * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
+ * does not contain loudness metadata or if it
+ * was not added before
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
public void removeMediaCodec(@NonNull MediaCodec mediaCodec) {
int piid = PLAYER_PIID_INVALID;
LoudnessCodecInfo mcInfo;
- AtomicBoolean removed = new AtomicBoolean(false);
+ AtomicBoolean removedMc = new AtomicBoolean(false);
+ AtomicBoolean removeInfo = new AtomicBoolean(false);
mcInfo = getCodecInfo(Objects.requireNonNull(mediaCodec,
"MediaCodec for removeMediaCodec cannot be null"));
- if (mcInfo != null) {
- synchronized (mConfiguratorLock) {
- if (mAudioTrack != null) {
- piid = mAudioTrack.getPlayerIId();
+ if (mcInfo == null) {
+ throw new IllegalArgumentException("Could not extract codec loudness information");
+ }
+ synchronized (mConfiguratorLock) {
+ if (mAudioTrack != null) {
+ piid = mAudioTrack.getPlayerIId();
+ }
+ mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
+ removedMc.set(mcs.remove(mediaCodec));
+ if (mcs.isEmpty()) {
+ // remove the entry
+ removeInfo.set(true);
+ return null;
}
- mMediaCodecs.computeIfPresent(mcInfo, (format, mcs) -> {
- removed.set(mcs.remove(mediaCodec));
- if (mcs.isEmpty()) {
- // remove the entry
- return null;
- }
- return mcs;
- });
+ return mcs;
+ });
+ if (!removedMc.get()) {
+ throw new IllegalArgumentException(
+ "Loudness configurator does not contain " + mediaCodec);
}
+ }
- if (piid != PLAYER_PIID_INVALID && removed.get()) {
- mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
- }
+ if (piid != PLAYER_PIID_INVALID && removeInfo.get()) {
+ mLcDispatcher.removeLoudnessCodecInfo(piid, mcInfo);
}
}
@@ -342,9 +333,6 @@ public class LoudnessCodecConfigurator {
*
* @return the {@link Bundle} containing the current loudness parameters. Caller is
* responsible to update the {@link MediaCodec}
- *
- * TODO: remove hide once API is final
- * @hide
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
@NonNull
@@ -375,9 +363,9 @@ public class LoudnessCodecConfigurator {
}
/** @hide */
- /*package*/ List<MediaCodec> getRegisteredMediaCodecList() {
+ /*package*/ Map<LoudnessCodecInfo, Set<MediaCodec>> getRegisteredMediaCodecs() {
synchronized (mConfiguratorLock) {
- return mMediaCodecs.values().stream().flatMap(Collection::stream).toList();
+ return mMediaCodecs;
}
}
@@ -397,40 +385,43 @@ public class LoudnessCodecConfigurator {
return null;
}
- final MediaFormat inputFormat = mediaCodec.getInputFormat();
- final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
- if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
- // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize one of
- // these two keys
- int aacProfile = -1;
- int profile = -1;
- try {
- aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
- } catch (NullPointerException e) {
- // does not contain KEY_AAC_PROFILE. do nothing
- }
- try {
- profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
- } catch (NullPointerException e) {
- // does not contain KEY_PROFILE. do nothing
- }
- if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
- || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
- lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+ try {
+ final MediaFormat inputFormat = mediaCodec.getInputFormat();
+ final String mimeType = inputFormat.getString(MediaFormat.KEY_MIME);
+ if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) {
+ // check both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize
+ // one of these two keys
+ int aacProfile = -1;
+ int profile = -1;
+ try {
+ aacProfile = inputFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
+ } catch (NullPointerException e) {
+ // does not contain KEY_AAC_PROFILE. do nothing
+ }
+ try {
+ profile = inputFormat.getInteger(MediaFormat.KEY_PROFILE);
+ } catch (NullPointerException e) {
+ // does not contain KEY_PROFILE. do nothing
+ }
+ if (aacProfile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE
+ || profile == MediaCodecInfo.CodecProfileLevel.AACObjectXHE) {
+ lci.metadataType = CODEC_METADATA_TYPE_MPEG_D;
+ } else {
+ lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+ }
} else {
- lci.metadataType = CODEC_METADATA_TYPE_MPEG_4;
+ Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+ return null;
}
- } else {
- Log.w(TAG, "MediaCodec mime type not supported for loudness annotation");
+
+ final MediaFormat outputFormat = mediaCodec.getOutputFormat();
+ lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
+ < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "MediaCodec is not configured", e);
return null;
}
- final MediaFormat outputFormat = mediaCodec.getOutputFormat();
- lci.isDownmixing = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
- < inputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
-
- lci.mediaCodecHashCode = mediaCodec.hashCode();
-
return lci;
}
}
diff --git a/media/java/android/media/LoudnessCodecDispatcher.java b/media/java/android/media/LoudnessCodecDispatcher.java
index be881b11e545..b546a81b0498 100644
--- a/media/java/android/media/LoudnessCodecDispatcher.java
+++ b/media/java/android/media/LoudnessCodecDispatcher.java
@@ -27,12 +27,16 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Log;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -52,6 +56,9 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub {
private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>
mConfiguratorListener = new HashMap<>();
@@ -66,38 +73,56 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub {
@Override
public void dispatchLoudnessCodecParameterChange(int piid, PersistableBundle params) {
- mLoudnessListenerMgr.callListeners(listener ->
+ if (DEBUG) {
+ Log.d(TAG, "dispatchLoudnessCodecParameterChange for piid " + piid
+ + " persistable bundle: " + params);
+ }
+ mLoudnessListenerMgr.callListeners(listener -> {
+ synchronized (mLock) {
mConfiguratorListener.computeIfPresent(listener, (l, lcConfig) -> {
// send the appropriate bundle for the user to update
if (lcConfig.getAssignedTrackPiid() == piid) {
- final List<MediaCodec> mediaCodecs =
- lcConfig.getRegisteredMediaCodecList();
- for (MediaCodec mediaCodec : mediaCodecs) {
- final String infoKey = Integer.toString(mediaCodec.hashCode());
+ final Map<LoudnessCodecInfo, Set<MediaCodec>> mediaCodecsMap =
+ lcConfig.getRegisteredMediaCodecs();
+ for (LoudnessCodecInfo codecInfo : mediaCodecsMap.keySet()) {
+ final String infoKey = Integer.toString(codecInfo.hashCode());
+ Bundle bundle = null;
if (params.containsKey(infoKey)) {
- Bundle bundle = new Bundle(
- params.getPersistableBundle(infoKey));
- if (DEBUG) {
- Log.d(TAG,
- "Received for piid " + piid + " bundle: " + bundle);
+ bundle = new Bundle(params.getPersistableBundle(infoKey));
+ }
+
+ final Set<MediaCodec> mediaCodecs = mediaCodecsMap.get(codecInfo);
+ for (MediaCodec mediaCodec : mediaCodecs) {
+ final String mediaCodecKey = Integer.toString(
+ mediaCodec.hashCode());
+ if (bundle == null && !params.containsKey(mediaCodecKey)) {
+ continue;
+ }
+ boolean canBreak = false;
+ if (bundle == null) {
+ // key was set by media codec hash to update single codec
+ bundle = new Bundle(
+ params.getPersistableBundle(mediaCodecKey));
+ canBreak = true;
}
bundle =
LoudnessCodecUpdatesDispatcherStub.filterLoudnessParams(
- l.onLoudnessCodecUpdate(mediaCodec, bundle));
- if (DEBUG) {
- Log.d(TAG, "User changed for piid " + piid
- + " to filtered bundle: " + bundle);
- }
+ l.onLoudnessCodecUpdate(mediaCodec,
+ bundle));
if (!bundle.isDefinitelyEmpty()) {
mediaCodec.setParameters(bundle);
}
+ if (canBreak) {
+ break;
+ }
}
}
}
-
return lcConfig;
- }));
+ });
+ }
+ });
}
private static Bundle filterLoudnessParams(Bundle bundle) {
@@ -130,21 +155,33 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub {
mLoudnessListenerMgr.addListener(
executor, listener, "addLoudnessCodecListener",
() -> dispatcher);
- mConfiguratorListener.put(listener, configurator);
+ synchronized (mLock) {
+ mConfiguratorListener.put(listener, configurator);
+ }
}
void removeLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator) {
Objects.requireNonNull(configurator);
- for (Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e :
- mConfiguratorListener.entrySet()) {
- if (e.getValue() == configurator) {
- final OnLoudnessCodecUpdateListener listener = e.getKey();
- mConfiguratorListener.remove(listener);
- mLoudnessListenerMgr.removeListener(listener, "removeLoudnessCodecListener");
- break;
+ OnLoudnessCodecUpdateListener listenerToRemove = null;
+ synchronized (mLock) {
+ Iterator<Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>> iterator =
+ mConfiguratorListener.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e =
+ iterator.next();
+ if (e.getValue() == configurator) {
+ final OnLoudnessCodecUpdateListener listener = e.getKey();
+ iterator.remove();
+ listenerToRemove = listener;
+ break;
+ }
}
}
+ if (listenerToRemove != null) {
+ mLoudnessListenerMgr.removeListener(listenerToRemove,
+ "removeLoudnessCodecListener");
+ }
}
}
@@ -202,9 +239,10 @@ public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub {
}
/** @hide */
- public void addLoudnessCodecInfo(int piid, @NonNull LoudnessCodecInfo mcInfo) {
+ public void addLoudnessCodecInfo(int piid, int mediaCodecHash,
+ @NonNull LoudnessCodecInfo mcInfo) {
try {
- mAudioService.addLoudnessCodecInfo(piid, mcInfo);
+ mAudioService.addLoudnessCodecInfo(piid, mediaCodecHash, mcInfo);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/LoudnessCodecInfo.aidl b/media/java/android/media/LoudnessCodecInfo.aidl
index fd695179057d..0ac5646a3047 100644
--- a/media/java/android/media/LoudnessCodecInfo.aidl
+++ b/media/java/android/media/LoudnessCodecInfo.aidl
@@ -23,7 +23,7 @@ package android.media;
*
* {@hide}
*/
-@JavaDerive(equals = true)
+@JavaDerive(equals = true, toString = true)
parcelable LoudnessCodecInfo {
/** Supported codec metadata types for loudness updates. */
@Backing(type="int")
@@ -37,7 +37,6 @@ parcelable LoudnessCodecInfo {
CODEC_METADATA_TYPE_DTS_UHD = 6
}
- int mediaCodecHashCode;
CodecMetadataType metadataType;
boolean isDownmixing;
} \ No newline at end of file
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
index aa5a290346b0..2b52c4b107b6 100644
--- a/media/java/android/media/tv/ad/TvAdManager.java
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -16,6 +16,10 @@
package android.media.tv.ad;
+import android.annotation.FlaggedApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.tv.flags.Flags;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -23,14 +27,17 @@ import android.util.Log;
/**
* Central system API to the overall client-side TV AD architecture, which arbitrates interaction
* between applications and AD services.
- * @hide
*/
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
+@SystemService(Context.TV_AD_SERVICE)
public class TvAdManager {
+ // TODO: implement more methods and unhide APIs.
private static final String TAG = "TvAdManager";
private final ITvAdManager mService;
private final int mUserId;
+ /** @hide */
public TvAdManager(ITvAdManager service, int userId) {
mService = service;
mUserId = userId;
@@ -38,6 +45,7 @@ public class TvAdManager {
/**
* The Session provides the per-session functionality of AD service.
+ * @hide
*/
public static final class Session {
private final IBinder mToken;
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index a73d1ff72a17..018eaf6dc32f 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -5,4 +5,11 @@ flag {
namespace: "media_tv"
description: "Constants for standardizing broadcast visibility types."
bug: "222402395"
+}
+
+flag {
+ name: "enable_ad_service_fw"
+ namespace: "media_tv"
+ description: "Enable the TV client-side AD framework."
+ bug: "303506816"
} \ No newline at end of file
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 65a9799431e7..c9e36b7f10bd 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -208,7 +208,7 @@ public class LoudnessCodecConfiguratorTest {
verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
mLcc.addMediaCodec(createAndConfigureMediaCodec());
- verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
}
@Test
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index fe26dc3d7feb..991fe41bb7f3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -16,10 +16,12 @@ android_app {
dex_preopt: {
profile_guided: true,
+ //TODO: b/312357299 - Update baseline profile
profile: "profile.txt.prof",
},
static_libs: [
+ "CredentialManagerShared",
"PlatformComposeCore",
"androidx.activity_activity-compose",
"androidx.appcompat_appcompat",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 42f1207c69cb..325d3f819f14 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
@@ -27,10 +28,10 @@ import com.android.credentialmanager.mapper.toGet
import com.android.credentialmanager.model.Request
fun Intent.parse(
- packageManager: PackageManager,
+ context: Context,
): Request {
- return parseCancelUiRequest(packageManager)
- ?: parseRequestInfo()
+ return parseCancelUiRequest(context.packageManager)
+ ?: parseRequestInfo(context)
}
fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
@@ -51,11 +52,11 @@ fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
}
}
-fun Intent.parseRequestInfo(): Request =
+fun Intent.parseRequestInfo(context: Context): Request =
requestInfo.let{ info ->
when (info?.type) {
RequestInfo.TYPE_CREATE -> Request.Create(info.token)
- RequestInfo.TYPE_GET -> toGet()
+ RequestInfo.TYPE_GET -> toGet(context)
else -> {
throw IllegalStateException("Unrecognized request type: ${info?.type}")
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 83183b5f58eb..3ef65b052560 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -16,8 +16,8 @@
package com.android.credentialmanager.client.impl
+import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.UserSelectionDialogResult
import android.os.Bundle
@@ -26,12 +26,13 @@ import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import com.android.credentialmanager.client.CredentialManagerClient
+import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
class CredentialManagerClientImpl @Inject constructor(
- private val packageManager: PackageManager,
+ @ApplicationContext private val context: Context,
) : CredentialManagerClient {
private val _requests = MutableStateFlow<Request?>(null)
@@ -40,7 +41,7 @@ class CredentialManagerClientImpl @Inject constructor(
override fun updateRequest(intent: Intent) {
val request = intent.parse(
- packageManager = packageManager,
+ context = context,
)
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
new file mode 100644
index 000000000000..f063074b39b4
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ktx
+
+import android.app.slice.Slice
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.credentials.Credential
+import android.credentials.flags.Flags
+import android.credentials.ui.AuthenticationEntry
+import android.credentials.ui.Entry
+import android.credentials.ui.GetCredentialProviderData
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
+import com.android.credentialmanager.TAG
+
+fun CredentialEntryInfo.getIntentSenderRequest(
+ isAutoSelected: Boolean = false
+): IntentSenderRequest? {
+ val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
+
+ return pendingIntent?.let{
+ IntentSenderRequest
+ .Builder(pendingIntent = it)
+ .setFillInIntent(entryIntent)
+ .build()
+ }
+}
+
+// Returns the list (potentially empty) of enabled provider.
+fun List<GetCredentialProviderData>.toProviderList(
+ context: Context,
+): List<ProviderInfo> {
+ val providerList: MutableList<ProviderInfo> = mutableListOf()
+ this.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(
+ ProviderInfo(
+ id = it.providerFlattenedComponentName,
+ icon = providerIcon,
+ displayName = providerLabel,
+ credentialEntryList = getCredentialOptionInfoList(
+ providerId = it.providerFlattenedComponentName,
+ providerLabel = providerLabel,
+ credentialEntries = it.credentialEntries,
+ context = context
+ ),
+ authenticationEntryList = getAuthenticationEntryList(
+ it.providerFlattenedComponentName,
+ providerLabel,
+ providerIcon,
+ it.authenticationEntries),
+ remoteEntry = getRemoteEntry(
+ it.providerFlattenedComponentName,
+ it.remoteEntry
+ ),
+ actionEntryList = getActionEntryList(
+ it.providerFlattenedComponentName, it.actionChips, providerIcon
+ ),
+ )
+ )
+ }
+ return providerList
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getCredentialOptionInfoList(
+ providerId: String,
+ providerLabel: String,
+ credentialEntries: List<Entry>,
+ context: Context,
+): List<CredentialEntryInfo> {
+ val result: MutableList<CredentialEntryInfo> = mutableListOf()
+ credentialEntries.forEach {
+ val credentialEntry = it.slice.credentialEntry
+ when (credentialEntry) {
+ is PasswordCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSWORD,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is PublicKeyCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSKEY,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is CustomCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.UNKNOWN,
+ credentialTypeDisplayName =
+ credentialEntry.typeDisplayName?.toString().orEmpty(),
+ userName = credentialEntry.title.toString(),
+ displayName = credentialEntry.subtitle?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ else -> Log.d(
+ TAG,
+ "Encountered unrecognized credential entry ${it.slice.spec?.type}"
+ )
+ }
+ }
+ return result
+}
+val Slice.credentialEntry: CredentialEntry?
+ get() =
+ try {
+ when (spec?.type) {
+ Credential.TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(this)!!
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+ PublicKeyCredentialEntry.fromSlice(this)!!
+
+ else -> CustomCredentialEntry.fromSlice(this)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ CustomCredentialEntry.fromSlice(this)
+ }
+
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getAuthenticationEntryList(
+ providerId: String,
+ providerDisplayName: String,
+ providerIcon: Drawable,
+ authEntryList: List<AuthenticationEntry>,
+): List<AuthenticationEntryInfo> {
+ val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
+ authEntryList.forEach { entry ->
+ val structuredAuthEntry =
+ AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
+
+ val title: String =
+ structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
+
+ result.add(
+ AuthenticationEntryInfo(
+ providerId = providerId,
+ entryKey = entry.key,
+ entrySubkey = entry.subkey,
+ pendingIntent = structuredAuthEntry.pendingIntent,
+ fillInIntent = entry.frameworkExtrasIntent,
+ title = title,
+ providerDisplayName = providerDisplayName,
+ icon = providerIcon,
+ isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
+ isLastUnlocked =
+ entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
+ )
+ )
+ }
+ return result
+}
+
+private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
+ if (remoteEntry == null) {
+ return null
+ }
+ val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
+ ?: return null
+ return RemoteEntryInfo(
+ providerId = providerId,
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ pendingIntent = structuredRemoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
+ )
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getActionEntryList(
+ providerId: String,
+ actionEntries: List<Entry>,
+ providerIcon: Drawable,
+): List<ActionEntryInfo> {
+ val result: MutableList<ActionEntryInfo> = mutableListOf()
+ actionEntries.forEach {
+ val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
+ result.add(
+ ActionEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = actionEntryUi.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ title = actionEntryUi.title.toString(),
+ icon = providerIcon,
+ subTitle = actionEntryUi.subtitle?.toString(),
+ )
+ )
+ }
+ return result
+}
+
+
+
+private fun getServiceLabelAndIcon(
+ pm: PackageManager,
+ providerFlattenedComponentName: String
+): Pair<String, Drawable>? {
+ var providerLabel: String? = null
+ var providerIcon: Drawable? = null
+ val component = ComponentName.unflattenFromString(providerFlattenedComponentName)
+ if (component == null) {
+ // Test data has only package name not component name.
+ // For test data usage only.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ } else {
+ try {
+ val si = pm.getServiceInfo(component, PackageManager.ComponentInfoFlags.of(0))
+ providerLabel = si.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = si.loadIcon(pm)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Provider service info not found", e)
+ // Added for mdoc use case where the provider may not need to register a service and
+ // instead only relies on the registration api.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ component.packageName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ }
+ }
+ return if (providerLabel == null || providerIcon == null) {
+ Log.d(
+ TAG,
+ "Failed to load provider label/icon for provider $providerFlattenedComponentName"
+ )
+ null
+ } else {
+ Pair(providerLabel, providerIcon)
+ }
+}
+
+private fun getPackageInfo(
+ pm: PackageManager,
+ packageName: String
+): PackageInfo {
+ val packageManagerFlags = PackageManager.MATCH_INSTANT
+
+ return pm.getPackageInfo(
+ packageName,
+ PackageManager.PackageInfoFlags.of(
+ (packageManagerFlags).toLong())
+ )
+} \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
deleted file mode 100644
index 34710704facb..000000000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0N
- *
- * 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.credentialmanager.ktx
-
-import androidx.activity.result.IntentSenderRequest
-import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
-import com.android.credentialmanager.model.Password
-
-fun Password.getIntentSenderRequest(
- isAutoSelected: Boolean = false
-): IntentSenderRequest {
- val entryIntent = entry.frameworkExtrasIntent
- entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
-
- return IntentSenderRequest.Builder(
- pendingIntent = passwordCredentialEntry.pendingIntent
- ).setFillInIntent(entryIntent).build()
-}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index d4bca2add6cb..f1f1f7ca842e 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -16,48 +16,18 @@
package com.android.credentialmanager.mapper
+import android.content.Context
import android.content.Intent
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-import com.android.credentialmanager.factory.fromSlice
import com.android.credentialmanager.ktx.getCredentialProviderDataList
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.ktx.resultReceiver
-import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.ktx.toProviderList
import com.android.credentialmanager.model.Request
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
-
-fun Intent.toGet(): Request.Get {
- val credentialEntries = mutableListOf<Pair<String, Entry>>()
- for (providerData in getCredentialProviderDataList) {
- for (credentialEntry in providerData.credentialEntries) {
- credentialEntries.add(
- Pair(providerData.providerFlattenedComponentName, credentialEntry)
- )
- }
- }
-
- val passwordEntries = mutableListOf<Password>()
- for ((providerId, entry) in credentialEntries) {
- val slice = fromSlice(entry.slice)
- if (slice is PasswordCredentialEntry) {
- passwordEntries.add(
- Password(
- providerId = providerId,
- entry = entry,
- passwordCredentialEntry = slice
- )
- )
- }
- }
+fun Intent.toGet(context: Context): Request.Get {
return Request.Get(
token = requestInfo?.token,
- resultReceiver = this.resultReceiver,
- providers = ImmutableMap.copyOf(
- getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
- ),
- passwordEntries = ImmutableList.copyOf(passwordEntries)
+ resultReceiver = resultReceiver,
+ providerInfos = getCredentialProviderDataList.toProviderList(context)
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
index cc92f60de048..3f85192d937f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
enum class CredentialType {
UNKNOWN, PASSKEY, PASSWORD,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
index ee36989b3da0..6d59f11d5643 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
import android.app.PendingIntent
import android.content.Intent
-open class BaseEntry (
+open class EntryInfo (
val providerId: String,
val entryKey: String,
val entrySubkey: String,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 2289ed7320ca..763646233324 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,11 +16,9 @@
package com.android.credentialmanager.model
-import android.credentials.ui.ProviderData
import android.os.IBinder
import android.os.ResultReceiver
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
+import com.android.credentialmanager.model.get.ProviderInfo
/**
* Represents the request made by the CredentialManager API.
@@ -51,8 +49,7 @@ sealed class Request private constructor(
data class Get(
override val token: IBinder?,
val resultReceiver: ResultReceiver?,
- val providers: ImmutableMap<String, ProviderData>,
- val passwordEntries: ImmutableList<Password>,
+ val providerInfos: List<ProviderInfo>,
) : Request(token)
/**
* Request to start the create credentials flow.
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
new file mode 100644
index 000000000000..d6189eb15ff3
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.creation
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CreateOptionInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val userProviderDisplayName: String,
+ val profileIcon: Drawable?,
+ val passwordCount: Int?,
+ val passkeyCount: Int?,
+ val totalCredentialCount: Int?,
+ val lastUsedTime: Instant,
+ val footerDescription: String?,
+ val allowAutoSelect: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
new file mode 100644
index 000000000000..7ee50d7c4e73
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.creation
+
+import android.app.PendingIntent
+import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
+
+class RemoteInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
new file mode 100644
index 000000000000..d9eee868c17f
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class ActionEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val icon: Drawable,
+ val subTitle: String?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
new file mode 100644
index 000000000000..01c394fc0d74
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class AuthenticationEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val providerDisplayName: String,
+ val icon: Drawable,
+ // The entry had been unlocked and turned out to be empty. Used to determine whether to
+ // show "Tap to unlock" or "No sign-in info" for this entry.
+ val isUnlockedAndEmpty: Boolean,
+ // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
+ val isLastUnlocked: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey, entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = false,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
new file mode 100644
index 000000000000..9725881e1c97
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CredentialEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
+ val credentialType: CredentialType,
+ /** Localized type value of this credential used for display purpose. */
+ val credentialTypeDisplayName: String,
+ val providerDisplayName: String,
+ val userName: String,
+ val displayName: String?,
+ val icon: Drawable?,
+ val shouldTintIcon: Boolean,
+ val lastUsedTimeMillis: Instant?,
+ val isAutoSelectable: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
new file mode 100644
index 000000000000..6da414617752
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+ /**
+ * Unique id (component name) of this provider.
+ * Not for display purpose - [displayName] should be used for ui rendering.
+ */
+ val id: String,
+ val icon: Drawable,
+ val displayName: String,
+ val credentialEntryList: List<CredentialEntryInfo>,
+ val authenticationEntryList: List<AuthenticationEntryInfo>,
+ val remoteEntry: RemoteEntryInfo?,
+ val actionEntryList: List<ActionEntryInfo>,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
new file mode 100644
index 000000000000..a68bf7413b25
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
+
+class RemoteEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+) \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bce86c477e77..6c5a984f20f7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -28,7 +28,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
-import com.android.credentialmanager.common.BaseEntry
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
@@ -47,7 +47,7 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum
data class UiState(
val createCredentialUiState: CreateCredentialUiState?,
val getCredentialUiState: GetCredentialUiState?,
- val selectedEntry: BaseEntry? = null,
+ val selectedEntry: EntryInfo? = null,
val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
val dialogState: DialogState = DialogState.ACTIVE,
// True if the UI has one and only one auto selectable entry. Its provider activity will be
@@ -115,12 +115,13 @@ class CredentialSelectorViewModel(
launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val entry = uiState.selectedEntry
- if (entry != null && entry.pendingIntent != null) {
+ val pendingIntent = entry?.pendingIntent
+ if (pendingIntent != null) {
Log.d(Constants.LOG_TAG, "Launching provider activity")
uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
val entryIntent = entry.fillInIntent
entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
- val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+ val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
.setFillInIntent(entryIntent).build()
try {
launcher.launch(intentSenderRequest)
@@ -201,7 +202,7 @@ class CredentialSelectorViewModel(
/**************************************************************************/
/***** Get Flow Callbacks *****/
/**************************************************************************/
- fun getFlowOnEntrySelected(entry: BaseEntry) {
+ fun getFlowOnEntrySelected(entry: EntryInfo) {
Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
uiState = if (entry.pendingIntent != null) {
@@ -363,7 +364,7 @@ class CredentialSelectorViewModel(
)
}
- fun createFlowOnEntrySelected(selectedEntry: BaseEntry) {
+ fun createFlowOnEntrySelected(selectedEntry: EntryInfo) {
val providerId = selectedEntry.providerId
val entryKey = selectedEntry.entryKey
val entrySubkey = selectedEntry.entrySubkey
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f0fa6c5c4dd2..fc3970de2cee 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -16,13 +16,10 @@
package com.android.credentialmanager
-import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
-import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
-import android.credentials.ui.AuthenticationEntry
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
import android.credentials.ui.Entry
@@ -32,36 +29,26 @@ import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.Log
import com.android.credentialmanager.common.Constants
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
-import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.model.creation.CreateOptionInfo
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
-import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
-import com.android.credentialmanager.getflow.ActionEntryInfo
-import com.android.credentialmanager.getflow.AuthenticationEntryInfo
-import com.android.credentialmanager.getflow.CredentialEntryInfo
-import com.android.credentialmanager.getflow.ProviderInfo
-import com.android.credentialmanager.getflow.RemoteEntryInfo
-import com.android.credentialmanager.getflow.TopBrandingContent
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.ktx.toProviderList
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import androidx.credentials.provider.Action
-import androidx.credentials.provider.AuthenticationAction
import androidx.credentials.provider.CreateEntry
-import androidx.credentials.provider.CredentialEntry
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
import android.credentials.flags.Flags
+import com.android.credentialmanager.getflow.TopBrandingContent
import java.time.Instant
@@ -179,43 +166,7 @@ class GetFlowUtils {
fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
context: Context,
- ): List<ProviderInfo> {
- val providerList: MutableList<ProviderInfo> = mutableListOf()
- providerDataList.forEach {
- val providerLabelAndIcon = getServiceLabelAndIcon(
- context.packageManager,
- it.providerFlattenedComponentName
- ) ?: return@forEach
- val (providerLabel, providerIcon) = providerLabelAndIcon
- providerList.add(
- ProviderInfo(
- id = it.providerFlattenedComponentName,
- icon = providerIcon,
- displayName = providerLabel,
- credentialEntryList = getCredentialOptionInfoList(
- providerId = it.providerFlattenedComponentName,
- providerLabel = providerLabel,
- credentialEntries = it.credentialEntries,
- context = context
- ),
- authenticationEntryList = getAuthenticationEntryList(
- it.providerFlattenedComponentName,
- providerLabel,
- providerIcon,
- it.authenticationEntries),
- remoteEntry = getRemoteEntry(
- it.providerFlattenedComponentName,
- it.remoteEntry
- ),
- actionEntryList = getActionEntryList(
- it.providerFlattenedComponentName, it.actionChips, providerIcon
- ),
- )
- )
- }
- return providerList
- }
-
+ ): List<ProviderInfo> = providerDataList.toProviderList(context)
fun toRequestDisplayInfo(
requestInfo: RequestInfo?,
context: Context,
@@ -254,178 +205,6 @@ class GetFlowUtils {
preferTopBrandingContent = preferTopBrandingContent,
)
}
-
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getCredentialOptionInfoList(
- providerId: String,
- providerLabel: String,
- credentialEntries: List<Entry>,
- context: Context,
- ): List<CredentialEntryInfo> {
- val result: MutableList<CredentialEntryInfo> = mutableListOf()
- credentialEntries.forEach {
- val credentialEntry = parseCredentialEntryFromSlice(it.slice)
- when (credentialEntry) {
- is PasswordCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSWORD,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is PublicKeyCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSKEY,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is CustomCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.UNKNOWN,
- credentialTypeDisplayName =
- credentialEntry.typeDisplayName?.toString().orEmpty(),
- userName = credentialEntry.title.toString(),
- displayName = credentialEntry.subtitle?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- else -> Log.d(
- Constants.LOG_TAG,
- "Encountered unrecognized credential entry ${it.slice.spec?.type}"
- )
- }
- }
- return result
- }
-
- /**
- * @hide
- */
- fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
- try {
- when (slice.spec?.type) {
- TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
- TYPE_PUBLIC_KEY_CREDENTIAL -> return PublicKeyCredentialEntry.fromSlice(slice)!!
- else -> return CustomCredentialEntry.fromSlice(slice)!!
- }
- } catch (e: Exception) {
- // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
- // password / passkey parsing attempt.
- return CustomCredentialEntry.fromSlice(slice)
- }
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getAuthenticationEntryList(
- providerId: String,
- providerDisplayName: String,
- providerIcon: Drawable,
- authEntryList: List<AuthenticationEntry>,
- ): List<AuthenticationEntryInfo> {
- val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
- authEntryList.forEach { entry ->
- val structuredAuthEntry =
- AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
-
- val title: String =
- structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
-
- result.add(AuthenticationEntryInfo(
- providerId = providerId,
- entryKey = entry.key,
- entrySubkey = entry.subkey,
- pendingIntent = structuredAuthEntry.pendingIntent,
- fillInIntent = entry.frameworkExtrasIntent,
- title = title,
- providerDisplayName = providerDisplayName,
- icon = providerIcon,
- isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
- isLastUnlocked =
- entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
- ))
- }
- return result
- }
-
- private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
- if (remoteEntry == null) {
- return null
- }
- val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
- ?: return null
- return RemoteEntryInfo(
- providerId = providerId,
- entryKey = remoteEntry.key,
- entrySubkey = remoteEntry.subkey,
- pendingIntent = structuredRemoteEntry.pendingIntent,
- fillInIntent = remoteEntry.frameworkExtrasIntent,
- )
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getActionEntryList(
- providerId: String,
- actionEntries: List<Entry>,
- providerIcon: Drawable,
- ): List<ActionEntryInfo> {
- val result: MutableList<ActionEntryInfo> = mutableListOf()
- actionEntries.forEach {
- val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
- result.add(ActionEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = actionEntryUi.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- title = actionEntryUi.title.toString(),
- icon = providerIcon,
- subTitle = actionEntryUi.subtitle?.toString(),
- ))
- }
- return result
- }
}
}
@@ -686,7 +465,8 @@ class CreateFlowUtils {
val result: MutableList<CreateOptionInfo> = mutableListOf()
creationEntries.forEach {
val createEntry = CreateEntry.fromSlice(it.slice) ?: return@forEach
- result.add(CreateOptionInfo(
+ result.add(
+ CreateOptionInfo(
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
@@ -705,7 +485,8 @@ class CreateFlowUtils {
it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
"SELECT_ALLOWED")
}?.text == "true",
- ))
+ )
+ )
}
return result.sortedWith(
compareByDescending { it.lastUsedTime }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 281696dec717..20d2f09ced8f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -48,10 +48,11 @@ import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.getflow.CredentialEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.getflow.ProviderInfo
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
+import com.android.credentialmanager.ktx.credentialEntry
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -122,8 +123,7 @@ class CredentialAutofillService : AutofillService() {
val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
candidateProviderDataList.forEach { provider ->
provider.credentialEntries.forEach { entry ->
- val credentialEntry = GetFlowUtils.parseCredentialEntryFromSlice(entry.slice)
- when (credentialEntry) {
+ when (val credentialEntry = entry.slice.credentialEntry) {
is PasswordCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
@@ -172,11 +172,11 @@ class CredentialAutofillService : AutofillService() {
}
private fun processProvidersForAutofillId(
- filLRequest: FillRequest,
- autofillId: AutofillId,
- providerList: List<ProviderInfo>,
- entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ filLRequest: FillRequest,
+ autofillId: AutofillId,
+ providerList: List<ProviderInfo>,
+ entryIconMap: Map<String, Icon>,
+ fillResponseBuilder: FillResponse.Builder
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -200,7 +200,8 @@ class CredentialAutofillService : AutofillService() {
providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
- if (pendingIntent == null || primaryEntry.fillInIntent == null) {
+ val fillInIntent = primaryEntry.fillInIntent
+ if (pendingIntent == null || fillInIntent == null) {
// FillInIntent will not be null because autofillId was retrieved from it.
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
@@ -245,7 +246,7 @@ class CredentialAutofillService : AutofillService() {
presentationBuilder.build())
.build())
.setAuthentication(pendingIntent.intentSender)
- .setAuthenticationExtras(primaryEntry.fillInIntent.extras)
+ .setAuthenticationExtras(fillInIntent.extras)
.build())
datasetAdded = true
}
@@ -322,8 +323,8 @@ class CredentialAutofillService : AutofillService() {
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
+ providerInfo: ProviderInfo,
+ credentialList: List<CredentialEntryInfo>
): ProviderInfo {
return ProviderInfo(
providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index d45b6f687193..14a91651753b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -48,8 +48,8 @@ import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +68,8 @@ import com.android.credentialmanager.common.ui.SheetContainerCard
import com.android.credentialmanager.common.ui.PasskeyBenefitRow
import com.android.credentialmanager.common.ui.HeadlineText
import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -259,15 +261,15 @@ fun PasskeyIntroCard(
@Composable
fun MoreOptionsSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- disabledProviderList: List<DisabledProviderInfo>?,
- sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- onBackCreationSelectionButtonSelected: () -> Unit,
- onOptionSelected: (ActiveEntry) -> Unit,
- onDisabledProvidersSelected: () -> Unit,
- onRemoteEntrySelected: (BaseEntry) -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ disabledProviderList: List<DisabledProviderInfo>?,
+ sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ onBackCreationSelectionButtonSelected: () -> Unit,
+ onOptionSelected: (ActiveEntry) -> Unit,
+ onDisabledProvidersSelected: () -> Unit,
+ onRemoteEntrySelected: (EntryInfo) -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard(topAppBar = {
MoreOptionTopAppBar(
@@ -378,14 +380,14 @@ fun NonDefaultUsageConfirmationCard(
@Composable
fun CreationSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- providerInfo: EnabledProviderInfo,
- createOptionInfo: CreateOptionInfo,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onMoreOptionsSelected: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ providerInfo: EnabledProviderInfo,
+ createOptionInfo: CreateOptionInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onMoreOptionsSelected: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item {
@@ -474,11 +476,11 @@ fun CreationSelectionCard(
@Composable
fun ExternalOnlySelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- activeRemoteEntry: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ activeRemoteEntry: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
@@ -575,17 +577,14 @@ fun MoreAboutPasskeysIntroCard(
@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
- entryInfo: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit
+ entryInfo: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit
) {
Entry(
onClick = { onOptionSelected(entryInfo) },
- iconImageBitmap =
- if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
- entryInfo.profileIcon.toBitmap().asImageBitmap()
- } else {
- requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
- },
+ iconImageBitmap = ((entryInfo as? CreateOptionInfo)?.profileIcon
+ ?: requestDisplayInfo.typeIcon)
+ .toBitmap().asImageBitmap(),
shouldApplyIconImageBitmapTint = !(entryInfo is CreateOptionInfo &&
entryInfo.profileIcon != null),
entryHeadlineText = requestDisplayInfo.title,
@@ -627,32 +626,33 @@ fun MoreOptionsInfoRow(
entryThirdLineText =
if (requestDisplayInfo.type == CredentialType.PASSKEY ||
requestDisplayInfo.type == CredentialType.PASSWORD) {
- if (createOptionInfo.passwordCount != null &&
- createOptionInfo.passkeyCount != null
- ) {
+ val passwordCount = createOptionInfo.passwordCount
+ val passkeyCount = createOptionInfo.passkeyCount
+ if (passwordCount != null && passkeyCount != null) {
stringResource(
R.string.more_options_usage_passwords_passkeys,
- createOptionInfo.passwordCount,
- createOptionInfo.passkeyCount
+ passwordCount,
+ passkeyCount
)
- } else if (createOptionInfo.passwordCount != null) {
+ } else if (passwordCount != null) {
stringResource(
R.string.more_options_usage_passwords,
- createOptionInfo.passwordCount
+ passwordCount
)
- } else if (createOptionInfo.passkeyCount != null) {
+ } else if (passkeyCount != null) {
stringResource(
R.string.more_options_usage_passkeys,
- createOptionInfo.passkeyCount
+ passkeyCount
)
} else {
null
}
} else {
- if (createOptionInfo.totalCredentialCount != null) {
+ val totalCredentialCount = createOptionInfo.totalCredentialCount
+ if (totalCredentialCount != null) {
stringResource(
R.string.more_options_usage_credentials,
- createOptionInfo.totalCredentialCount
+ totalCredentialCount
)
} else {
null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index e9e8c2e0ccbf..8b0ba87fa9be 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -16,22 +16,21 @@
package com.android.credentialmanager.createflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
-import java.time.Instant
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
data class CreateCredentialUiState(
- val enabledProviders: List<EnabledProviderInfo>,
- val disabledProviders: List<DisabledProviderInfo>? = null,
- val currentScreenState: CreateScreenState,
- val requestDisplayInfo: RequestDisplayInfo,
- val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- val activeEntry: ActiveEntry? = null,
- val remoteEntry: RemoteInfo? = null,
- val foundCandidateFromUserDefaultProvider: Boolean,
+ val enabledProviders: List<EnabledProviderInfo>,
+ val disabledProviders: List<DisabledProviderInfo>? = null,
+ val currentScreenState: CreateScreenState,
+ val requestDisplayInfo: RequestDisplayInfo,
+ val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ val activeEntry: ActiveEntry? = null,
+ val remoteEntry: RemoteInfo? = null,
+ val foundCandidateFromUserDefaultProvider: Boolean,
)
internal fun isFlowAutoSelectable(
@@ -75,44 +74,6 @@ class DisabledProviderInfo(
displayName: String,
) : ProviderInfo(icon, id, displayName)
-class CreateOptionInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val userProviderDisplayName: String,
- val profileIcon: Drawable?,
- val passwordCount: Int?,
- val passkeyCount: Int?,
- val totalCredentialCount: Int?,
- val lastUsedTime: Instant,
- val footerDescription: String?,
- val allowAutoSelect: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class RemoteInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val title: String,
val subtitle: String?,
@@ -131,8 +92,8 @@ data class RequestDisplayInfo(
* user selects a different entry on the more option page.
*/
data class ActiveEntry (
- val activeProvider: EnabledProviderInfo,
- val activeEntryInfo: BaseEntry,
+ val activeProvider: EnabledProviderInfo,
+ val activeEntryInfo: EntryInfo,
)
/** The name of the current screen. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 72d030b3e657..4ed84b908865 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -48,8 +48,9 @@ import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +69,10 @@ import com.android.credentialmanager.common.ui.HeadlineIcon
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.credentialmanager.userAndDisplayNameForPasskey
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -175,8 +180,8 @@ fun PrimarySelectionCard(
requestDisplayInfo: RequestDisplayInfo,
providerDisplayInfo: ProviderDisplayInfo,
providerInfoList: List<ProviderInfo>,
- activeEntry: BaseEntry?,
- onEntrySelected: (BaseEntry) -> Unit,
+ activeEntry: EntryInfo?,
+ onEntrySelected: (EntryInfo) -> Unit,
onConfirm: () -> Unit,
onMoreOptionSelected: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -358,7 +363,7 @@ fun PrimarySelectionCard(
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
onBackButtonClicked: () -> Unit,
onCancel: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -436,7 +441,7 @@ fun HeadlineProviderIconAndName(
@Composable
fun ActionChips(
providerInfoList: List<ProviderInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
val actionChips = providerInfoList.flatMap { it.actionEntryList }
@@ -460,7 +465,7 @@ fun ActionChips(
@Composable
fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -486,7 +491,7 @@ fun RemoteEntryCard(
@Composable
fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -508,7 +513,7 @@ fun LockedCredentials(
@Composable
fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -532,7 +537,7 @@ fun PerUserNameCredentials(
@Composable
fun CredentialEntryRow(
credentialEntryInfo: CredentialEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
) {
@@ -571,7 +576,7 @@ fun CredentialEntryRow(
@Composable
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
) {
Entry(
@@ -596,7 +601,7 @@ fun AuthenticationEntryRow(
@Composable
fun ActionEntryRow(
actionEntryInfo: ActionEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
) {
ActionEntry(
iconImageBitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 447a9d2aaa8d..46bebc4073ab 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,21 +16,21 @@
package com.android.credentialmanager.getflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.internal.util.Preconditions
-import java.time.Instant
-
data class GetCredentialUiState(
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
- val activeEntry: BaseEntry? = toActiveEntry(providerDisplayInfo),
+ val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
val isNoAccount: Boolean = false,
)
@@ -58,20 +58,6 @@ internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): Cred
return null
}
-data class ProviderInfo(
- /**
- * Unique id (component name) of this provider.
- * Not for display purpose - [displayName] should be used for ui rendering.
- */
- val id: String,
- val icon: Drawable,
- val displayName: String,
- val credentialEntryList: List<CredentialEntryInfo>,
- val authenticationEntryList: List<AuthenticationEntryInfo>,
- val remoteEntry: RemoteEntryInfo?,
- val actionEntryList: List<ActionEntryInfo>,
-)
-
/** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping
* by the provider id but instead focuses on structures convenient for display purposes. */
data class ProviderDisplayInfo(
@@ -84,87 +70,6 @@ data class ProviderDisplayInfo(
val remoteEntry: RemoteEntryInfo?
)
-class CredentialEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
- val credentialType: CredentialType,
- /** Localized type value of this credential used for display purpose. */
- val credentialTypeDisplayName: String,
- val providerDisplayName: String,
- val userName: String,
- val displayName: String?,
- val icon: Drawable?,
- val shouldTintIcon: Boolean,
- val lastUsedTimeMillis: Instant?,
- val isAutoSelectable: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class AuthenticationEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val providerDisplayName: String,
- val icon: Drawable,
- // The entry had been unlocked and turned out to be empty. Used to determine whether to
- // show "Tap to unlock" or "No sign-in info" for this entry.
- val isUnlockedAndEmpty: Boolean,
- // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
- val isLastUnlocked: Boolean,
-) : BaseEntry(
- providerId,
- entryKey, entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = false,
-)
-
-class RemoteEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class ActionEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val icon: Drawable,
- val subTitle: String?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val appName: String,
val preferImmediatelyAvailableCredentials: Boolean,
@@ -218,8 +123,8 @@ fun toProviderDisplayInfo(
val remoteEntryList = mutableListOf<RemoteEntryInfo>()
providerInfoList.forEach { providerInfo ->
authenticationEntryList.addAll(providerInfo.authenticationEntryList)
- if (providerInfo.remoteEntry != null) {
- remoteEntryList.add(providerInfo.remoteEntry)
+ providerInfo.remoteEntry?.let {
+ remoteEntryList.add(it)
}
// There can only be at most one remote entry
Preconditions.checkState(remoteEntryList.size <= 1)
@@ -260,11 +165,11 @@ fun toProviderDisplayInfo(
private fun toActiveEntry(
providerDisplayInfo: ProviderDisplayInfo,
-): BaseEntry? {
+): EntryInfo? {
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- var activeEntry: BaseEntry? = null
+ var activeEntry: EntryInfo? = null
if (sortedUserNameToCredentialEntryList
.size == 1 && authenticationEntryList.isEmpty()
) {
@@ -302,17 +207,18 @@ internal class CredentialEntryInfoComparatorByTypeThenTimestamp : Comparator<Cre
return 1
}
}
-
+ val p0LastUsedTimeMillis = p0.lastUsedTimeMillis
+ val p1LastUsedTimeMillis = p1.lastUsedTimeMillis
// Then order by last used timestamp
- if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
- if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+ if (p0LastUsedTimeMillis != null && p1LastUsedTimeMillis != null) {
+ if (p0LastUsedTimeMillis < p1LastUsedTimeMillis) {
return 1
- } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+ } else if (p0LastUsedTimeMillis > p1LastUsedTimeMillis) {
return -1
}
- } else if (p0.lastUsedTimeMillis != null) {
+ } else if (p0LastUsedTimeMillis != null) {
return -1
- } else if (p1.lastUsedTimeMillis != null) {
+ } else if (p1LastUsedTimeMillis != null) {
return 1
}
return 0
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
index 6ededf3411bf..d8a6019b68a2 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -1,29 +1,20 @@
package com.android.credentialmanager.di
-import android.content.Context
-import android.content.pm.PackageManager
import com.android.credentialmanager.client.CredentialManagerClient
import com.android.credentialmanager.client.impl.CredentialManagerClientImpl
+import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-internal object AppModule {
- @Provides
+abstract class AppModule {
+ @Binds
@Singleton
- @JvmStatic
- fun providePackageManager(@ApplicationContext context: Context): PackageManager =
- context.packageManager
-
- @Provides
- @Singleton
- @JvmStatic
- fun provideCredentialManagerClient(packageManager: PackageManager): CredentialManagerClient =
- CredentialManagerClientImpl(packageManager)
+ abstract fun provideCredentialManagerClient(
+ client: CredentialManagerClientImpl
+ ): CredentialManagerClient
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index f2f878e8ac2f..14b992a0d0e9 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -23,8 +23,8 @@ fun Request.Get.toGet(): CredentialSelectorUiState.Get {
// TODO: b/301206470 returning a hard coded state for MVP
if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
- return if (providers.size == 1) {
- if (passwordEntries.size == 1) {
+ return if (providerInfos.size == 1) {
+ if (providerInfos.first().credentialEntryList.size == 1) {
CredentialSelectorUiState.Get.SingleProviderSinglePassword
} else {
TODO() // b/301206470 - Implement other get flows
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c28df3e8895a..b64f58192d23 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -76,7 +76,9 @@ fun SinglePasswordScreen(
}
SideEffect {
- launcher.launch(state.intentSenderRequest)
+ state.intentSenderRequest?.let {
+ launcher.launch(it)
+ }
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index fb72c544c978..26bee1f8d191 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -26,9 +26,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.getIntentSenderRequest
-import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.model.PasswordUiModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -44,7 +44,7 @@ class SinglePasswordScreenViewModel @Inject constructor(
private var initializeCalled = false
private lateinit var requestGet: Request.Get
- private lateinit var password: Password
+ private lateinit var entryInfo: CredentialEntryInfo
private val _uiState =
MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
@@ -63,14 +63,15 @@ class SinglePasswordScreenViewModel @Inject constructor(
_uiState.value = SinglePasswordScreenUiState.Error
} else {
requestGet = request
- if (requestGet.passwordEntries.isEmpty()) {
+
+ if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
Log.d(TAG, "Empty passwordEntries")
_uiState.value = SinglePasswordScreenUiState.Error
} else {
- password = requestGet.passwordEntries.first()
+ entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
_uiState.value = SinglePasswordScreenUiState.Loaded(
PasswordUiModel(
- email = password.passwordCredentialEntry.username.toString(),
+ email = entryInfo.userName,
)
)
}
@@ -84,7 +85,7 @@ class SinglePasswordScreenViewModel @Inject constructor(
fun onOKClick() {
_uiState.value = SinglePasswordScreenUiState.PasswordSelected(
- intentSenderRequest = password.getIntentSenderRequest()
+ intentSenderRequest = entryInfo.getIntentSenderRequest()
)
}
@@ -94,9 +95,9 @@ class SinglePasswordScreenViewModel @Inject constructor(
) {
val userSelectionDialogResult = UserSelectionDialogResult(
requestGet.token,
- password.providerId,
- password.entry.key,
- password.entry.subkey,
+ entryInfo.providerId,
+ entryInfo.entryKey,
+ entryInfo.entrySubkey,
if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
)
credentialManagerClient.sendResult(userSelectionDialogResult)
@@ -108,7 +109,7 @@ sealed class SinglePasswordScreenUiState {
data object Idle : SinglePasswordScreenUiState()
data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
data class PasswordSelected(
- val intentSenderRequest: IntentSenderRequest
+ val intentSenderRequest: IntentSenderRequest?
) : SinglePasswordScreenUiState()
data object Cancel : SinglePasswordScreenUiState()
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 38bd7d5f3944..6213b34034af 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -49,6 +49,7 @@ android_app {
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
lint: {
@@ -75,6 +76,7 @@ android_app {
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tablet"],
@@ -103,6 +105,7 @@ android_app {
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tv"],
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
index 19d74b33e034..7b17cbdd3a1e 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -16,8 +16,6 @@
package com.android.packageinstaller;
-import static android.content.Intent.CATEGORY_LAUNCHER;
-
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.Activity;
@@ -47,9 +45,6 @@ public class DeleteStagedFileOnResult extends Activity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(resultCode, data);
finish();
- if (data != null && data.hasCategory(CATEGORY_LAUNCHER)) {
- startActivity(data);
- }
}
@Override
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index e2107ebe2525..418705845065 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -16,12 +16,14 @@
package com.android.packageinstaller;
+import static android.content.pm.Flags.usePiaV2;
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -57,14 +59,21 @@ public class InstallStart extends Activity {
private final boolean mLocalLOGV = false;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (USE_PIA_V2) {
+ mPackageManager = getPackageManager();
+ if (usePiaV2()) {
+ Log.i(TAG, "Using Pia V2");
+
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.v2.model.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getCallingPackage());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
@@ -74,7 +83,6 @@ public class InstallStart extends Activity {
finish();
return;
}
- mPackageManager = getPackageManager();
mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index 9af88c3b4694..215ead367148 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -18,6 +18,7 @@ package com.android.packageinstaller;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -120,7 +121,12 @@ public class InstallSuccess extends Activity {
Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
- setResult(Activity.RESULT_OK, mLaunchIntent);
+ try {
+ startActivity(mLaunchIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP));
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Log.e(LOG_TAG, "Could not start activity", e);
+ }
finish();
});
} else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 34062a4cbde6..ba627e9e9202 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -17,8 +17,8 @@
package com.android.packageinstaller;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.content.pm.Flags.usePiaV2;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
@@ -81,9 +81,6 @@ public class UninstallerActivity extends Activity {
private String mPackageName;
private DialogInfo mDialogInfo;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
public void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -92,7 +89,18 @@ public class UninstallerActivity extends Activity {
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- if (USE_PIA_V2 && !isTv()) {
+ if (usePiaV2() && !isTv()) {
+ Log.i(TAG, "Using Pia V2");
+
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(
+ new ComponentName(this, com.android.packageinstaller.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ pm.setComponentEnabledSetting(
+ new ComponentName(this,
+ com.android.packageinstaller.v2.model.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(UninstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 4871ef3097a5..010a6ce9d4d9 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -17,7 +17,6 @@ android_library {
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
- "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 4b3acbf2c394..e70114f36e97 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -17,5 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget.mainswitch">
-
+ <uses-sdk android:minSdkVersion="21" />
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 2b5fcd807899..e6f61a8400c4 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -18,6 +18,8 @@ package com.android.settingslib.widget;
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -30,7 +32,6 @@ import android.widget.TextView;
import androidx.annotation.ColorInt;
-import com.android.settingslib.utils.BuildCompatUtils;
import com.android.settingslib.widget.mainswitch.R;
import java.util.ArrayList;
@@ -72,11 +73,18 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
- if (!BuildCompatUtils.isAtLeastS()) {
- final TypedArray a = context.obtainStyledAttributes(
- new int[]{android.R.attr.colorAccent});
- mBackgroundActivatedColor = a.getColor(0, 0);
- mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
+ TypedArray a;
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ a = context.obtainStyledAttributes(
+ new int[]{android.R.attr.colorAccent});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ } else {
+ a = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = a.getColor(0, 0);
+ }
a.recycle();
}
@@ -148,7 +156,7 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
* Set icon space reserved for title
*/
public void setIconSpaceReserved(boolean iconSpaceReserved) {
- if (mTextView != null && !BuildCompatUtils.isAtLeastS()) {
+ if (mTextView != null && (Build.VERSION.SDK_INT < VERSION_CODES.S)) {
LayoutParams params = (LayoutParams) mTextView.getLayoutParams();
int iconSpace = getContext().getResources().getDimensionPixelSize(
R.dimen.settingslib_switchbar_subsettings_margin_start);
@@ -207,7 +215,7 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
mTextView.setEnabled(enabled);
mSwitch.setEnabled(enabled);
- if (BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.S) {
mFrameView.setEnabled(enabled);
mFrameView.setActivated(isChecked());
}
@@ -222,7 +230,7 @@ public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListen
}
private void setBackground(boolean isChecked) {
- if (!BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor);
} else {
mFrameView.setActivated(isChecked);
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 80b944f5749f..60bf48c8b75e 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -25,11 +25,11 @@ plugins {
alias(libs.plugins.kotlin.android) apply false
}
-val androidTop : String = File(rootDir, "../../../../..").canonicalPath
+val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.6.0-alpha08"
+ extra["jetpackComposeVersion"] = "1.6.0-beta01"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 4335173dd706..acd90f3c4f4d 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@ dependencies {
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-alpha10")
+ api("androidx.compose.material3:material3:1.2.0-alpha11")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.5")
+ api("androidx.navigation:navigation-compose:2.7.4")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index 26372b6bfd32..c395558b769c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -17,9 +17,7 @@
package com.android.settingslib.spa.framework.theme
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.ripple.LocalRippleTheme
-import androidx.compose.material.ripple.RippleAlpha
-import androidx.compose.material.ripple.RippleTheme
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -39,7 +37,7 @@ fun SettingsTheme(content: @Composable () -> Unit) {
MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) {
CompositionLocalProvider(
LocalColorScheme provides settingsColorScheme(isDarkTheme),
- LocalRippleTheme provides SettingsRippleTheme,
+ LocalContentColor provides MaterialTheme.colorScheme.onSurface,
) {
content()
}
@@ -52,19 +50,3 @@ object SettingsTheme {
@ReadOnlyComposable
get() = LocalColorScheme.current
}
-
-private object SettingsRippleTheme : RippleTheme {
- @Composable
- override fun defaultColor() = MaterialTheme.colorScheme.onSurface
-
- @Composable
- override fun rippleAlpha() = RippleAlpha
-}
-
-/** Alpha levels for all content. */
-private val RippleAlpha = RippleAlpha(
- pressedAlpha = 0.48f,
- focusedAlpha = 0.48f,
- draggedAlpha = 0.32f,
- hoveredAlpha = 0.16f,
-)
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 0a2d9fc3372e..e41126f03c60 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -140,12 +140,6 @@ public class PrimarySwitchPreference extends RestrictedPreference {
}
}
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- setSwitchEnabled(enabled);
- }
-
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public boolean isSwitchEnabled() {
return mEnableSwitch;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 8b0f19d455d2..29ea25e13835 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -22,7 +22,6 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -237,15 +236,13 @@ public class RestrictedPreferenceHelper {
}
private void updateDisabledState() {
- boolean isEnabled = !(mDisabledByAdmin || mDisabledByAppOps);
if (!(mPreference instanceof RestrictedTopLevelPreference)) {
- mPreference.setEnabled(isEnabled);
+ mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
}
- Drawable icon = mPreference.getIcon();
- if (!isEnabled && icon != null) {
- Utils.convertToGrayscale(icon);
- mPreference.setIcon(icon);
+ if (mPreference instanceof PrimarySwitchPreference) {
+ ((PrimarySwitchPreference) mPreference)
+ .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f03263b71138..107d5f8a8ae9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -728,14 +728,4 @@ public class Utils {
return false;
}
- /**
- * Convert a drawable to grayscale drawable
- */
- public static void convertToGrayscale(@NonNull Drawable drawable) {
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0.0f);
-
- ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
- drawable.setColorFilter(filter);
- }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efed8c3c1ef4..85e87691ac85 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -235,6 +235,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
+ Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.DISABLE_WINDOW_BLURS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.BATTERY_TIP_CONSTANTS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d78038ecee61..7cf562f48ff3 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -242,184 +242,10 @@ filegroup {
}
filegroup {
- name: "SystemUI-test-fakes",
- srcs: [
- /* Status bar fakes */
- "tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt",
-
- /* QS fakes */
- "tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt",
- ],
- path: "tests/src",
-}
-
-filegroup {
- name: "SystemUI-tests-robolectric-pilots",
- srcs: [
- /* Keyguard converted tests */
- // data
- "tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt",
- "tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt",
- // domain
- "tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
- "tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt",
- // ui
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
- // Keyguard helper
- "tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt",
- "tests/src/com/android/systemui/dock/DockManagerFake.java",
- "tests/src/com/android/systemui/dump/LogBufferHelper.kt",
- "tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java",
-
- /* Biometric converted tests */
- "tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt",
- "tests/src/com/android/systemui/biometrics/AuthControllerTest.java",
- "tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java",
- "tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt",
- "tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java",
- "tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt",
- "tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt",
-
- /* Status bar wifi converted tests */
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt",
- "tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt",
-
- /* Bouncer UI tests */
- "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java",
- "tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java",
- "tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java",
- "tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt",
- "tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt",
-
- /* Communal tests */
- "tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt",
- "tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt",
- "tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt",
-
- /* Dream tests */
- "tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java",
- "tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java",
- "tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java",
- "tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java",
- "tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt",
- "tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt",
- "tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java",
- "tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java",
-
- /* Smartspace tests */
- "tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt",
- "tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt",
- "tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt",
- "tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt",
-
- /* Quick Settings new pipeline converted tests */
- "tests/src/com/android/systemui/qs/pipeline/data/**/*Test.kt",
- "tests/src/com/android/systemui/qs/pipeline/domain/**/*Test.kt",
- "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
- "tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
- "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
- "tests/src/com/android/systemui/qs/tiles/impl/**/*.kt",
-
- /* Authentication */
- "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt",
- "tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt",
-
- /* Device entry */
- "tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt",
- "tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt",
-
- /* Bouncer scene */
- "tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt",
- "tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt",
-
- /* Lockscreen scene */
- "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt",
-
- /* Shade scene */
- "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt",
- "tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt",
-
- /* Quick Settings scene */
- "tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt",
-
- /* Flexiglass / Scene framework tests */
- "tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt",
- "tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt",
- "tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt",
- "tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt",
- "tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt",
- "tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt",
- "tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt",
-
- ],
- path: "tests/src",
-}
-
-filegroup {
name: "SystemUI-tests-multivalent",
srcs: [
"multivalentTests/src/**/*.kt",
+ "multivalentTests/src/**/*.java",
],
path: "multivalentTests/src",
}
@@ -597,8 +423,6 @@ android_robolectric_test {
"tests/robolectric/src/**/*.kt",
"tests/robolectric/src/**/*.java",
":SystemUI-tests-utils",
- ":SystemUI-test-fakes",
- ":SystemUI-tests-robolectric-pilots",
":SystemUI-tests-multivalent",
],
static_libs: [
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0480b9dbfc8e..0c89a5dcbcf4 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -126,24 +126,6 @@
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
}
],
@@ -170,18 +152,6 @@
"include-annotation": "androidx.test.filters.FlakyTest"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
}
]
}
diff --git a/packages/SystemUI/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS
index e1a7a0f84ad0..902ba907a100 100644
--- a/packages/SystemUI/aconfig/OWNERS
+++ b/packages/SystemUI/aconfig/OWNERS
@@ -1 +1,2 @@
per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
+per-file biometrics_framework.aconfig = file:/services/core/java/com/android/server/biometrics/OWNERS
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
new file mode 100644
index 000000000000..5fd3b485e9ed
--- /dev/null
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.systemui"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+ name: "bp_talkback"
+ namespace: "biometrics_framework"
+ description: "Adds talkback directional guidance when using UDFPS with biometric prompt"
+ bug: "310044658"
+} \ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a9dc145afabd..3e84597a375d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -114,6 +114,13 @@ flag {
}
flag {
+ name: "unfold_animation_background_progress"
+ namespace: "systemui"
+ description: "Moves unfold animation progress calculation to a background thread"
+ bug: "277879146"
+}
+
+flag {
name: "qs_new_pipeline"
namespace: "systemui"
description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
@@ -157,3 +164,9 @@ flag {
bug: "296122467"
}
+flag {
+ name: "record_issue_qs_tile"
+ namespace: "systemui"
+ description: "Replace Record Trace QS Tile with expanded Record Issue QS Tile"
+ bug: "305049544"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 0b1338305076..eb0688914b9d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -16,12 +16,10 @@
package com.android.systemui.bouncer.ui.composable
+import android.view.ViewTreeObserver
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
@@ -30,46 +28,56 @@ import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.core.view.WindowInsetsCompat
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
+ val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+ LaunchedEffect(isTextFieldFocusRequested) {
+ if (isTextFieldFocusRequested) {
+ focusRequester.requestFocus()
+ }
+ }
+ val (isTextFieldFocused, onTextFieldFocusChanged) = remember { mutableStateOf(false) }
+ LaunchedEffect(isTextFieldFocused) {
+ viewModel.onTextFieldFocusChanged(isFocused = isTextFieldFocused)
+ }
+
val password: String by viewModel.password.collectAsState()
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
- val density = LocalDensity.current
- val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
+ val isImeVisible by isSoftwareKeyboardVisible()
LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
DisposableEffect(Unit) {
viewModel.onShown()
-
- // When the UI comes up, request focus on the TextField to bring up the software keyboard.
- focusRequester.requestFocus()
-
onDispose { viewModel.onHidden() }
}
@@ -104,16 +112,39 @@ internal fun PasswordBouncer(
onDone = { viewModel.onAuthenticateKeyPressed() },
),
modifier =
- Modifier.focusRequester(focusRequester).drawBehind {
- drawLine(
- color = color,
- start = Offset(x = 0f, y = size.height - lineWidthPx),
- end = Offset(size.width, y = size.height - lineWidthPx),
- strokeWidth = lineWidthPx,
- )
- },
+ Modifier.focusRequester(focusRequester)
+ .onFocusChanged { onTextFieldFocusChanged(it.isFocused) }
+ .drawBehind {
+ drawLine(
+ color = color,
+ start = Offset(x = 0f, y = size.height - lineWidthPx),
+ end = Offset(size.width, y = size.height - lineWidthPx),
+ strokeWidth = lineWidthPx,
+ )
+ },
)
Spacer(Modifier.height(100.dp))
}
}
+
+/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
+@Composable
+fun isSoftwareKeyboardVisible(): State<Boolean> {
+ val view = LocalView.current
+ val viewTreeObserver = view.viewTreeObserver
+
+ return produceState(
+ initialValue = false,
+ key1 = viewTreeObserver,
+ ) {
+ val listener =
+ ViewTreeObserver.OnGlobalLayoutListener {
+ value = view.rootWindowInsets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
+ }
+
+ viewTreeObserver.addOnGlobalLayoutListener(listener)
+
+ awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 3fbcf6d77f82..3fbcf6d77f82 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 74c922561343..74c922561343 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index d41c2497b230..d41c2497b230 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 80d45bcc23dd..80d45bcc23dd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 543b2910bbda..543b2910bbda 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 156e06843d15..156e06843d15 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 7bb6ef1c8895..7bb6ef1c8895 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 94c3bde29597..94c3bde29597 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 7b1f302da6e8..7b1f302da6e8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 64ddbc7828ac..64ddbc7828ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 56d3d260d196..56d3d260d196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 602f3dc29491..602f3dc29491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
index 714461b715d6..714461b715d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 15633d1baed1..15633d1baed1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index 39f0d570cb26..39f0d570cb26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index a1b801cd3d3f..a1b801cd3d3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index f5b6f14a627c..f5b6f14a627c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e2cab29c473c..e2cab29c473c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index cd9189bef7f1..cd9189bef7f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
index 5239966f1923..5239966f1923 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 2ea803c6aa8f..2ea803c6aa8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index 98d8b054716c..98d8b054716c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 79f062536404..79f062536404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
index 8b374ae54127..8b374ae54127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 9fbe09619ff1..9fbe09619ff1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
index b391b5a45799..b391b5a45799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/SimBouncerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index f0d26b6bbb78..f0d26b6bbb78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 1e8073246f98..83fb17fa50e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -319,10 +319,10 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun imeHiddenEvent_isTriggered() =
testScope.runTest {
- val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+ val imeHiddenEvent by collectLastValue(underTest.onImeHiddenByUser)
runCurrent()
- underTest.onImeHidden()
+ underTest.onImeHiddenByUser()
runCurrent()
assertThat(imeHiddenEvent).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index 4aea4f329858..4aea4f329858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index bdf5041f8a38..bdf5041f8a38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
index 8c53c0e3f267..8c53c0e3f267 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 63c992bd7854..45c186dc3a77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -23,8 +23,6 @@ import com.android.systemui.authentication.data.repository.FakeAuthenticationRep
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
@@ -76,18 +74,4 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(animateFailure).isFalse()
}
-
- @Test
- fun onImeVisibilityChanged() =
- testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
-
- underTest.onImeVisibilityChanged(true)
- assertThat(onImeHidden).isNull()
-
- underTest.onImeVisibilityChanged(false)
- assertThat(onImeHidden).isNotNull()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 75d6a007b4aa..75d6a007b4aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 90e0c19b7c65..90e0c19b7c65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 9b1e9585979a..937c703d6775 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -20,7 +20,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -43,7 +45,11 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor = utils.authenticationInteractor()
+ private val authenticationRepository = utils.authenticationRepository
+ private val authenticationInteractor =
+ utils.authenticationInteractor(
+ repository = authenticationRepository,
+ )
private val sceneInteractor = utils.sceneInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
@@ -207,6 +213,101 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ @Test
+ fun onImeVisibilityChanged_false_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_emitsOnImeHiddenByUserEvent() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(2)
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+ underTest.onImeVisibilityChanged(isVisible = true)
+ setThrottling(true)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_initiallyTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusGained_becomesFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLost_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ setThrottling(false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
@@ -226,6 +327,35 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
switchToScene(SceneKey.Bouncer)
}
+ private suspend fun TestScope.setThrottling(
+ isThrottling: Boolean,
+ failedAttemptCount: Int = 5,
+ ) {
+ if (isThrottling) {
+ repeat(failedAttemptCount) {
+ authenticationRepository.reportAuthenticationAttempt(false)
+ }
+ val remainingTimeMs = 30_000
+ authenticationRepository.setThrottleDuration(remainingTimeMs)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = remainingTimeMs,
+ )
+ )
+ } else {
+ authenticationRepository.reportAuthenticationAttempt(true)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = 0,
+ )
+ )
+ }
+
+ runCurrent()
+ }
+
companion object {
private const val ENTER_YOUR_PASSWORD = "Enter your password"
private const val WRONG_PASSWORD = "Wrong password"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 862c39c9d4cc..862c39c9d4cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index c30e405ab911..c30e405ab911 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
index 55016bb1fc07..55016bb1fc07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 182712a13174..182712a13174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 16cfa2398fd5..16cfa2398fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index a49629252520..a49629252520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 97ac8c62d69d..97ac8c62d69d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 0004f52bc1c1..910097eece52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -56,6 +57,13 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
)
@Test
+ fun canSwipeToEnter_startsNull() =
+ testScope.runTest {
+ val values by collectValues(underTest.canSwipeToEnter)
+ assertThat(values[0]).isNull()
+ }
+
+ @Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
index 37540621557f..37540621557f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dock/DockManagerFake.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 8a35ef11a364..8a35ef11a364 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
index 2c6c793c97f5..2c6c793c97f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 2af6566e993a..2af6566e993a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
index d379dc6f3dc1..d379dc6f3dc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index e5f997257cfa..e5f997257cfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 6d5cd49b8af6..6d5cd49b8af6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
index 7ff345cdcf7e..7ff345cdcf7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 39db2beb4c44..39db2beb4c44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
index 315a24bfd945..315a24bfd945 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
index e0c6ab20c6e1..e0c6ab20c6e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index 480754c17616..480754c17616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3d1efa59a11b..3d1efa59a11b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
index 6aa821f15ab1..6aa821f15ab1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
index 017fdbe8ac01..017fdbe8ac01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
index 4ee4a60fbeaf..4ee4a60fbeaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
index 0538227abd3f..0538227abd3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/LogBufferHelper.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 977f1db44258..977f1db44258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 548b5646f5d4..548b5646f5d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index 4ae144c03314..4ae144c03314 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 7d68cc0a3560..7d68cc0a3560 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb234843d..26fcb234843d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 99a01858471c..99a01858471c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index a1c9f87ee7bc..a1c9f87ee7bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index b15352bfe6ab..b15352bfe6ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 521dea34513e..521dea34513e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 02db0d7a9a50..02db0d7a9a50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index a9b9c9011636..a9b9c9011636 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 4587ea6dbdc8..4587ea6dbdc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 45aca175657e..45aca175657e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 2b7221ec192c..2b7221ec192c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
index ae6c5b7b36b0..ae6c5b7b36b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 567e0a9717fc..567e0a9717fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 6c4bb372bc3a..6c4bb372bc3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 7242cb20dc77..7242cb20dc77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index ee47c58f4002..ee47c58f4002 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 706f94e412ac..706f94e412ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 98f0211587ea..98f0211587ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index bc4bae0ed959..bc4bae0ed959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 4f7d9444020c..4f7d9444020c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index b483085cf1e5..b483085cf1e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index fd125e099f1b..fd125e099f1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index cf2012989624..cf2012989624 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index d07836d3abce..d07836d3abce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index ba72b4c95a44..ba72b4c95a44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 3536d5c77c93..3536d5c77c93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index d0772270ed5e..d0772270ed5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 6cab023d59b0..6cab023d59b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index d277fcab3690..d277fcab3690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
index 3db676d68f42..3db676d68f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 070e07a75d23..070e07a75d23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
index ff8a9bd019fb..ff8a9bd019fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index f7c3b213730c..f7c3b213730c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
index 9516c2181ac0..9516c2181ac0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
index 36e860e37ffa..36e860e37ffa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
index d4a9fabd6806..d4a9fabd6806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
index 4454a3cb15fc..4454a3cb15fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index d153e9d1d361..d153e9d1d361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
index ec139e4c515e..ec139e4c515e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index 4fae532d4174..4fae532d4174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
index 9e2d1f885e2d..9e2d1f885e2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
index 0116bd9575d8..0116bd9575d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
index e7ea9a66450c..e7ea9a66450c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
index 20fd3601f9ef..20fd3601f9ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
index 19ac63c36cab..19ac63c36cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
index d645ee34619b..d645ee34619b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
index 83ff35d8022d..83ff35d8022d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index adccc84e494b..adccc84e494b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index 41a7ec03408d..41a7ec03408d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 8c896a6a1709..8c896a6a1709 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index 7e0e7d1f46e8..7e0e7d1f46e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
index 0d9711588a1f..0d9711588a1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index f73cab8a10a3..f73cab8a10a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
index 558e7694b54c..558e7694b54c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
index bd1c310ab8de..bd1c310ab8de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
index 9861606fd1b1..9861606fd1b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
index 2bdc154dd885..2bdc154dd885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 92c2d743c262..92c2d743c262 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
index 937744db500e..937744db500e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileDataInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
index 81bde8188f5e..81bde8188f5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplate/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
index 89ba69fce9ad..89ba69fce9ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
index 4a221134ce67..4a221134ce67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
new file mode 100644
index 000000000000..cf076c557765
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileRepositoryTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val persister = FakeCustomTileStatePersister()
+
+ private val underTest: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ TileSpec.create(TEST_COMPONENT),
+ persister,
+ testScope.testScheduler,
+ )
+
+ @Test
+ fun persistableTileIsRestoredForUser() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ persister.persistState(TEST_TILE_KEY_2, TEST_TILE_2)
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun notPersistableTileIsNotRestored() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, false)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun emptyPersistedStateIsHandled() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithPersistableTilePersists() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingWithNotPersistableTileDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithTileEmits() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingPeristableWithDefaultsPersists() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingNotPersistableWithDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updatingPeristableWithErrorDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithDefaultsEmits() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun getTileForAnotherUserReturnsNull() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isNull()
+ }
+
+ @Test
+ fun getTilesForAnotherUserEmpty() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithTileForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithTileForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ @Test
+ fun updatingWithDefaultsForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithDefaultsForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+
+ val TEST_USER_1 = UserHandle.of(1)!!
+ val TEST_TILE_1 =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_TILE_KEY_1 = TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier)
+ val TEST_DEFAULTS_1 = CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label)
+
+ val TEST_USER_2 = UserHandle.of(2)!!
+ val TEST_TILE_2 =
+ Tile().apply {
+ label = "test_tile_2"
+ icon = Icon.createWithContentUri("file://test_2")
+ }
+ val TEST_TILE_KEY_2 = TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier)
+ val TEST_DEFAULTS_2 = CustomTileDefaults.Result(TEST_TILE_2.icon, TEST_TILE_2.label)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
new file mode 100644
index 000000000000..eebb145ef384
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.text.format.DateUtils
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+
+ private val testScope = TestScope()
+
+ private val defaultsRepository = FakeCustomTileDefaultsRepository()
+ private val customTileStatePersister = FakeCustomTileStatePersister()
+ private val customTileRepository =
+ FakeCustomTileRepository(
+ TEST_TILE_SPEC,
+ customTileStatePersister,
+ testScope.testScheduler,
+ )
+
+ private lateinit var underTest: CustomTileInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ CustomTileInteractor(
+ TEST_USER,
+ defaultsRepository,
+ customTileRepository,
+ tileServiceManager,
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ )
+ }
+
+ @Test
+ fun activeTileIsAvailableAfterRestored() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+
+ underTest.init()
+
+ assertThat(underTest.tile).isEqualTo(TEST_TILE)
+ assertThat(underTest.tiles.first()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ underTest.updateTile(TEST_TILE)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterDefaultsUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ defaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
+ defaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun getTileBeforeInitThrows() = testScope.runTest { underTest.tile }
+
+ @Test
+ fun initSuspendsForActiveTileNotRestoredAndNotUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun initSuspendedForNotActiveTileWithoutUpdates() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+ val TEST_TILE_SPEC = TileSpec.create(TEST_COMPONENT)
+ val TEST_USER = UserHandle.of(1)!!
+ val TEST_TILE =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_DEFAULTS = CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index fab290da2953..7b2ac90b9766 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@ import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTile
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
@@ -37,7 +35,7 @@ import org.junit.runner.RunWith
class FlashlightMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
- private val mapper by lazy { FlashlightMapper(context) }
+ private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -58,12 +56,7 @@ class FlashlightMapperTest : SysuiTestCase() {
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_on,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
@@ -73,12 +66,7 @@ class FlashlightMapperTest : SysuiTestCase() {
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_off,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 820c0564c1a7..8791877f8863 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@ import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileMode
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth
import junit.framework.Assert
import org.junit.Test
@@ -37,7 +35,8 @@ import org.junit.runner.RunWith
class LocationTileMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
- private val mapper by lazy { LocationTileMapper(context) }
+
+ private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -57,9 +56,7 @@ class LocationTileMapperTest : SysuiTestCase() {
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_on, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
@@ -69,9 +66,7 @@ class LocationTileMapperTest : SysuiTestCase() {
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_off, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
index 5eca8caa7d15..5eca8caa7d15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
index 3a0ebdbd6a17..3a0ebdbd6a17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
index 22fb152aee44..22fb152aee44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 42e27ba12f42..42e27ba12f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c3294ff2e26c..18b7168098ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.authentication.data.repository.FakeAuthenticationRep
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -61,7 +62,6 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -273,6 +273,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
}
@Test
+ fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) }
+
+ @Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
emulateUserDrivenTransition(SceneKey.Bouncer)
@@ -336,7 +339,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
+ assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue()
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
@@ -775,11 +778,11 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private suspend fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
- bouncerViewModel.authMethodViewModel.value?.apply {
+ (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
if (showImeBeforeDismissing) {
- onImeVisibilityChanged(true)
+ it.onImeVisibilityChanged(true)
}
- onImeVisibilityChanged(false)
+ it.onImeVisibilityChanged(false)
runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index ddeb05b39e53..ddeb05b39e53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
index 7ae501d05fcd..7ae501d05fcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 7f4bbbe36768..7f4bbbe36768 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index 8be4eeb7be7a..8be4eeb7be7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c4ec56c906c3..3cb97e369a67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -145,6 +145,18 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun startsInLockscreenScene() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState()
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceLocks() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
@@ -467,7 +479,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
underTest.start()
runCurrent()
- bouncerInteractor.onImeHidden()
+ bouncerInteractor.onImeHiddenByUser()
runCurrent()
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index c89cd9e0c1f1..c89cd9e0c1f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index 77ddf15c41ad..77ddf15c41ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index e2640af136a7..e2640af136a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index cb83e7c7adbc..cb83e7c7adbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index e09385934991..e09385934991 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 886c61ae29dd..886c61ae29dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
index 0b5aea7d8683..0b5aea7d8683 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
index c669c6f6fb1c..c669c6f6fb1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
index 638925d0a705..638925d0a705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 5f4d7bf6f371..5f4d7bf6f371 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 75d1869adc7c..75d1869adc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
index a80238167b85..a80238167b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeSubscriptionManagerProxy.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 28d632d9fcea..28d632d9fcea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 106b54891948..106b54891948 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index d06a6e26b4ce..d06a6e26b4ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
index ce00250467f6..ce00250467f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index cf20ba87e8c2..cf20ba87e8c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 7fbbfc77300e..7fbbfc77300e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 791a028eef4e..791a028eef4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 9c08f5ef4cfe..355e75d0716b 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -18,7 +18,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
- <item android:id="@+id/notification_background_color_layer">
+ <item>
<shape>
<solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
</shape>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index af29cada2657..50241cdca8b5 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -111,107 +111,57 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle"
- app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
+ app:layout_constraintBottom_toTopOf="@+id/see_all_button" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/see_all_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_arrow,see_all_text" />
-
- <View
- android:id="@+id/see_all_clickable_row"
+ <Button
+ android:id="@+id/see_all_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/device_list"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" />
-
- <ImageView
- android:id="@+id/ic_arrow"
- android:layout_marginStart="36dp"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:importantForAccessibility="no"
- android:gravity="center_vertical"
- android:src="@drawable/ic_arrow_forward"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/see_all_text"
- app:layout_constraintTop_toBottomOf="@id/device_list" />
-
- <TextView
- android:id="@+id/see_all_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_button"
+ android:drawableStart="@drawable/ic_arrow_forward"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/see_all_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toEndOf="@+id/ic_arrow"
- app:layout_constraintTop_toBottomOf="@id/device_list"
- app:layout_constraintEnd_toEndOf="parent" />
-
- <androidx.constraintlayout.widget.Group
- android:id="@+id/pair_new_device_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
- <View
- android:id="@+id/pair_new_device_clickable_row"
+ <Button
+ android:id="@+id/pair_new_device_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/see_all_text"
- app:layout_constraintBottom_toTopOf="@+id/done_button" />
-
- <ImageView
- android:id="@+id/ic_add"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="36dp"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:src="@drawable/ic_add"
- app:layout_constraintBottom_toTopOf="@id/done_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- android:tint="?android:attr/textColorPrimary" />
-
- <TextView
- android:id="@+id/pair_new_device_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_button"
+ app:layout_constraintBottom_toTopOf="@+id/done_button"
+ android:drawableStart="@drawable/ic_add"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/pair_new_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintStart_toEndOf="@+id/ic_add"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- app:layout_constraintEnd_toEndOf="parent" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
<Button
android:id="@+id/done_button"
@@ -227,7 +177,7 @@
android:maxLines="1"
android:text="@string/inline_done_button"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/pair_new_device_button"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index d9385c7d24c4..bcc3c83b4560 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -104,4 +104,7 @@
<!-- Internet Dialog -->
<color name="connected_network_primary_color">@color/material_dynamic_primary80</color>
<color name="connected_network_secondary_color">@color/material_dynamic_secondary80</color>
+
+ <!-- Keyboard shortcut helper dialog -->
+ <color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e124aa0eda37..5f6a39a91b8b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -119,7 +119,7 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
- <color name="ksh_key_item_color">?androidprv:attr/materialColorOnSurfaceVariant</color>
+ <color name="ksh_key_item_color">@*android:color/system_on_surface_variant_light</color>
<color name="ksh_key_item_background">?androidprv:attr/materialColorSurfaceContainerHighest</color>
<color name="instant_apps_color">#ff4d5a64</color>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
index c9e57b45612c..b33f6fa7eadb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
@@ -32,8 +32,7 @@ constructor(private val activityManager: ActivityManager) : CurrentActivityTypeP
override val isHomeActivity: Boolean?
get() = _isHomeActivity
- private var _isHomeActivity: Boolean? = null
-
+ @Volatile private var _isHomeActivity: Boolean? = null
override fun init() {
_isHomeActivity = activityManager.isOnHomeActivity()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
index 3b8d318a3a79..baa88897947c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
@@ -18,18 +18,19 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class DeviceStateManagerFoldProvider @Inject constructor(
- private val deviceStateManager: DeviceStateManager,
- private val context: Context
-) : FoldProvider {
+class DeviceStateManagerFoldProvider
+@Inject
+constructor(private val deviceStateManager: DeviceStateManager, private val context: Context) :
+ FoldProvider {
- private val callbacks: MutableMap<FoldCallback,
- DeviceStateManager.DeviceStateCallback> = hashMapOf()
+ private val callbacks =
+ ConcurrentHashMap<FoldCallback, DeviceStateManager.DeviceStateCallback>()
override fun registerCallback(callback: FoldCallback, executor: Executor) {
val listener = FoldStateListener(context, callback)
@@ -39,13 +40,9 @@ class DeviceStateManagerFoldProvider @Inject constructor(
override fun unregisterCallback(callback: FoldCallback) {
val listener = callbacks.remove(callback)
- listener?.let {
- deviceStateManager.unregisterCallback(it)
- }
+ listener?.let { deviceStateManager.unregisterCallback(it) }
}
- private inner class FoldStateListener(
- context: Context,
- listener: FoldCallback
- ) : DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) })
+ private inner class FoldStateListener(context: Context, listener: FoldCallback) :
+ DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) })
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
index 7b67e3f3c920..7af991743457 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -15,24 +15,29 @@
package com.android.systemui.unfold.system
import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Process
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import dagger.Binds
import dagger.Module
+import dagger.Provides
import java.util.concurrent.Executor
+import javax.inject.Singleton
/**
- * Dagger module with system-only dependencies for the unfold animation.
- * The code that is used to calculate unfold transition progress
- * depends on some hidden APIs that are not available in normal
- * apps. In order to re-use this code and use alternative implementations
- * of these classes in other apps and hidden APIs here.
+ * Dagger module with system-only dependencies for the unfold animation. The code that is used to
+ * calculate unfold transition progress depends on some hidden APIs that are not available in normal
+ * apps. In order to re-use this code and use alternative implementations of these classes in other
+ * apps and hidden APIs here.
*/
@Module
abstract class SystemUnfoldSharedModule {
@@ -61,4 +66,22 @@ abstract class SystemUnfoldSharedModule {
@Binds
@UnfoldSingleThreadBg
abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor
+
+ companion object {
+ @Provides
+ @UnfoldBg
+ @Singleton
+ fun unfoldBgProgressHandler(@UnfoldBg looper: Looper): Handler {
+ return Handler(looper)
+ }
+
+ @Provides
+ @UnfoldBg
+ @Singleton
+ fun provideBgLooper(): Looper {
+ return HandlerThread("UnfoldBg", Process.THREAD_PRIORITY_FOREGROUND)
+ .apply { start() }
+ .looper
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index b704f3c89330..1edb551eb944 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -71,7 +71,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
private final DisplayTracker mDisplayTracker;
private final AccessibilityLogger mA11yLogger;
- private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
+ private MagnificationConnectionImpl mMagnificationConnectionImpl;
private SysUiState mSysUiState;
@VisibleForTesting
@@ -220,7 +220,7 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
}
@MainThread
- void setScale(int displayId, float scale) {
+ void setScaleForWindowMagnification(int displayId, float scale) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
@@ -321,37 +321,37 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
}
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
}
}
@Override
public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
}
}
@Override
public void onMove(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onMove(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onMove(displayId);
}
}
@@ -394,8 +394,8 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
@Override
public void onMagnifierScale(int displayId, float scale,
boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
mA11yLogger.logThrottled(
@@ -454,8 +454,8 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
if (magnificationSettingsController != null) {
magnificationSettingsController.closeMagnificationSettings();
}
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
}
}
}
@@ -500,12 +500,12 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
}
private void setWindowMagnificationConnection() {
- if (mWindowMagnificationConnectionImpl == null) {
- mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
+ if (mMagnificationConnectionImpl == null) {
+ mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
mHandler);
}
mAccessibilityManager.setWindowMagnificationConnection(
- mWindowMagnificationConnectionImpl);
+ mMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index 5666851f560f..5f0d496dd5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
*
* @see IWindowMagnificationConnection
*/
-class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
@@ -40,7 +40,7 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
private final Magnification mMagnification;
private final Handler mHandler;
- WindowMagnificationConnectionImpl(@NonNull Magnification magnification,
+ MagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
mMagnification = magnification;
mHandler = mainHandler;
@@ -57,8 +57,8 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
@Override
- public void setScale(int displayId, float scale) {
- mHandler.post(() -> mMagnification.setScale(displayId, scale));
+ public void setScaleForWindowMagnification(int displayId, float scale) {
+ mHandler.post(() -> mMagnification.setScaleForWindowMagnification(displayId, scale));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 1ac4163649a4..ab23564a1df4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -168,7 +169,7 @@ public class AuthContainerView extends LinearLayout
// HAT received from LockSettingsService when credential is verified.
@Nullable private byte[] mCredentialAttestation;
- // TODO(b/287311775): remove when legacy prompt is replaced
+ // TODO(b/313469218): remove when legacy prompt is replaced
@Deprecated
static class Config {
Context mContext;
@@ -220,6 +221,9 @@ public class AuthContainerView extends LinearLayout
mHandler.postDelayed(() -> {
addCredentialView(false /* animatePanel */, true /* animateContents */);
}, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS);
+
+ // TODO(b/313469218): Remove Config
+ mConfig.mPromptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 11a5d8b578df..3defec5ca48d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -485,8 +485,7 @@ constructor(
): Int =
if (isPendingConfirmation) {
when (sensorType) {
- FingerprintSensorType.POWER_BUTTON ->
- R.string.security_settings_sfps_enroll_find_sensor_message
+ FingerprintSensorType.POWER_BUTTON -> -1
else -> R.string.fingerprint_dialog_authenticated_confirmation
}
} else if (isAuthenticating || isAuthenticated) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b598631c3b57..7c46339ec103 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -105,9 +105,9 @@ constructor(
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
- private val _onImeHidden = MutableSharedFlow<Unit>()
- /** Provide the onImeHidden events from the bouncer */
- val onImeHidden: SharedFlow<Unit> = _onImeHidden
+ private val _onImeHiddenByUser = MutableSharedFlow<Unit>()
+ /** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
+ val onImeHiddenByUser: SharedFlow<Unit> = _onImeHiddenByUser
init {
if (flags.isEnabled()) {
@@ -230,9 +230,9 @@ constructor(
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** Notifies the interactor that the input method editor has been hidden. */
- suspend fun onImeHidden() {
- _onImeHidden.emit(Unit)
+ /** Notifies that the input method editor (software keyboard) has been hidden by the user. */
+ suspend fun onImeHiddenByUser() {
+ _onImeHiddenByUser.emit(Unit)
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 80248744c25a..e379dab918ef 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -46,9 +46,6 @@ sealed class AuthMethodBouncerViewModel(
*/
val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
- /** Whether the input method editor (for example, the software keyboard) is visible. */
- private var isImeVisible: Boolean = false
-
/** The authentication method that corresponds to this view model. */
abstract val authenticationMethod: AuthenticationMethodModel
@@ -68,7 +65,7 @@ sealed class AuthMethodBouncerViewModel(
/**
* Notifies that the UI has been hidden from the user (after any transitions have completed).
*/
- fun onHidden() {
+ open fun onHidden() {
clearInput()
interactor.resetMessage()
}
@@ -79,18 +76,6 @@ sealed class AuthMethodBouncerViewModel(
}
/**
- * Notifies that the input method editor (for example, the software keyboard) has been shown or
- * hidden.
- */
- suspend fun onImeVisibilityChanged(isVisible: Boolean) {
- if (isImeVisible && !isVisible) {
- interactor.onImeHidden()
- }
-
- isImeVisible = isVisible
- }
-
- /**
* Notifies that the failure animation has been shown. This should be called to consume a `true`
* value in [animateFailure].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index a15698e1f90c..45d181285df7 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -21,8 +21,11 @@ import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
@@ -45,6 +48,32 @@ class PasswordBouncerViewModel(
override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+ /** Whether the input method editor (for example, the software keyboard) is visible. */
+ private var isImeVisible: Boolean = false
+
+ /** Whether the text field element currently has focus. */
+ private val isTextFieldFocused = MutableStateFlow(false)
+
+ /** Whether the UI should request focus on the text field element. */
+ val isTextFieldFocusRequested =
+ combine(
+ interactor.isThrottled,
+ isTextFieldFocused,
+ ) { isThrottled, hasFocus ->
+ !isThrottled && !hasFocus
+ }
+ .stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !interactor.isThrottled.value && !isTextFieldFocused.value,
+ )
+
+ override fun onHidden() {
+ super.onHidden()
+ isImeVisible = false
+ isTextFieldFocused.value = false
+ }
+
override fun clearInput() {
_password.value = ""
}
@@ -72,4 +101,21 @@ class PasswordBouncerViewModel(
tryAuthenticate()
}
}
+
+ /**
+ * Notifies that the input method editor (for example, the software keyboard) has been shown or
+ * hidden.
+ */
+ suspend fun onImeVisibilityChanged(isVisible: Boolean) {
+ if (isImeVisible && !isVisible && !interactor.isThrottled.value) {
+ interactor.onImeHiddenByUser()
+ }
+
+ isImeVisible = isVisible
+ }
+
+ /** Notifies that the password text field has gained or lost focus. */
+ fun onTextFieldFocusChanged(isFocused: Boolean) {
+ isTextFieldFocused.value = isFocused
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d9e06296e460..e7b87730f94b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -19,6 +19,7 @@ package com.android.systemui.dagger;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.PerUser;
@@ -35,6 +36,7 @@ import com.android.systemui.unfold.FoldStateLogger;
import com.android.systemui.unfold.FoldStateLoggingProvider;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.dagger.UnfoldBg;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
@@ -144,7 +146,15 @@ public interface SysUIComponent {
getConnectingDisplayViewModel().init();
getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
getFoldStateLogger().ifPresent(FoldStateLogger::init);
- getUnfoldTransitionProgressProvider()
+
+ Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider;
+
+ if (Flags.unfoldAnimationBackgroundProgress()) {
+ unfoldTransitionProgressProvider = getBgUnfoldTransitionProgressProvider();
+ } else {
+ unfoldTransitionProgressProvider = getUnfoldTransitionProgressProvider();
+ }
+ unfoldTransitionProgressProvider
.ifPresent(
(progressProvider) ->
getUnfoldTransitionProgressForwarder()
@@ -170,7 +180,14 @@ public interface SysUIComponent {
ContextComponentHelper getContextComponentHelper();
/**
- * Creates a UnfoldTransitionProgressProvider.
+ * Creates a UnfoldTransitionProgressProvider that calculates progress in the background.
+ */
+ @SysUISingleton
+ @UnfoldBg
+ Optional<UnfoldTransitionProgressProvider> getBgUnfoldTransitionProgressProvider();
+
+ /**
+ * Creates a UnfoldTransitionProgressProvider that calculates progress in the main thread.
*/
@SysUISingleton
Optional<UnfoldTransitionProgressProvider> getUnfoldTransitionProgressProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 715fb17c7c2d..4cddb9ccffdb 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -103,8 +103,11 @@ constructor(
initialValue = false,
)
- // Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
- private val passivelyAuthenticated =
+ /**
+ * Whether the user is currently authenticated by a TrustAgent like trusted device, location,
+ * etc., or by face auth.
+ */
+ private val isPassivelyAuthenticated =
merge(
trustRepository.isCurrentUserTrusted,
deviceEntryFaceAuthRepository.isAuthenticated,
@@ -117,25 +120,31 @@ constructor(
* mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
* dismissed.
*
+ * A value of `null` is meaningless and is used as placeholder while the actual value is still
+ * being loaded in the background.
+ *
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
- val canSwipeToEnter =
+ val canSwipeToEnter: StateFlow<Boolean?> =
combine(
// This is true when the user has chosen to show the lockscreen but has not made it
// secure.
authenticationInteractor.authenticationMethod.map {
it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
- passivelyAuthenticated,
+ isPassivelyAuthenticated,
isDeviceEntered
- ) { isSwipeAuthMethod, passivelyAuthenticated, isDeviceEntered ->
- (isSwipeAuthMethod || passivelyAuthenticated) && !isDeviceEntered
+ ) { isSwipeAuthMethod, isPassivelyAuthenticated, isDeviceEntered ->
+ (isSwipeAuthMethod || isPassivelyAuthenticated) && !isDeviceEntered
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ // Starts as null to prevent downstream collectors from falsely assuming that the
+ // user can or cannot swipe to enter the device while the real value is being loaded
+ // from upstream data sources.
+ initialValue = null,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 26c5ea6e164d..c93b8e1a48f2 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -29,7 +29,6 @@ import com.android.app.tracing.FlowTracing.traceEach
import com.android.app.tracing.traceSection
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.util.Compile
@@ -93,7 +92,7 @@ class DisplayRepositoryImpl
constructor(
private val displayManager: DisplayManager,
@Background backgroundHandler: Handler,
- @Application applicationScope: CoroutineScope,
+ @Background bgApplicationScope: CoroutineScope,
@Background backgroundCoroutineDispatcher: CoroutineDispatcher
) : DisplayRepository {
private val allDisplayEvents: Flow<DisplayEvent> =
@@ -141,8 +140,7 @@ constructor(
private val enabledDisplays =
allDisplayEvents
.map { getDisplays() }
- .flowOn(backgroundCoroutineDispatcher)
- .shareIn(applicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+ .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
override val displays: Flow<Set<Display>> = enabledDisplays
@@ -203,9 +201,8 @@ constructor(
}
.distinctUntilChanged()
.debugLog("connectedDisplayIds")
- .flowOn(backgroundCoroutineDispatcher)
.stateIn(
- applicationScope,
+ bgApplicationScope,
started = SharingStarted.WhileSubscribed(),
// The initial value is set to empty, but connected displays are gathered as soon as
// the flow starts being collected. This is to ensure the call to get displays (an
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2a0d6a8a77d9..0c24752c5345 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -545,28 +545,12 @@ object Flags {
unreleasedFlag("clipboard_shared_transitions", teamfood = true)
/**
- * Whether the scene container (Flexiglass) is enabled. Note that [SCENE_CONTAINER] should be
- * checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can remove
- * unused code from our APK at compile time.
+ * Whether the scene container (Flexiglass) is enabled. Note that SceneContainerFlags#isEnabled
+ * should be checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can
+ * remove unused code from our APK at compile time.
*/
// TODO(b/283300105): Tracking Bug
@JvmField val SCENE_CONTAINER_ENABLED = false
- @Deprecated(
- message = """
- Do not use this flag directly. Please use
- [com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled].
-
- (Not really deprecated but using this as a simple way to bring attention to the above).
- """,
- replaceWith = ReplaceWith(
- "com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled",
- ),
- level = DeprecationLevel.WARNING,
- )
- @JvmField val SCENE_CONTAINER = resourceBooleanFlag(
- R.bool.config_sceneContainerFrameworkEnabled,
- "scene_container",
- )
// 1900
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index f9b89b11cd67..7354cfcb170d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.app.tracing.traceSection
+import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
import javax.inject.Singleton
@@ -29,7 +30,7 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL
screenLifecycle.addObserver(this)
}
- private val listeners: MutableList<ScreenListener> = mutableListOf()
+ private val listeners: MutableList<ScreenListener> = CopyOnWriteArrayList()
override fun removeCallback(listener: ScreenListener) {
listeners.remove(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index bd73d60cda29..62a0b0ebc08c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -131,13 +131,16 @@ constructor(
when (toState) {
KeyguardState.DREAMING -> TO_DREAMING_DURATION
KeyguardState.AOD -> TO_AOD_DURATION
+ KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
}
+
companion object {
private val DEFAULT_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
val TO_AOD_DURATION = 1300.milliseconds
+ val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 152d2172ee4c..cbfd17ff7ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -380,6 +380,8 @@ constructor(
KeyguardState.DREAMING -> TO_DREAMING_DURATION
KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
KeyguardState.AOD -> TO_AOD_DURATION
+ KeyguardState.DOZING -> TO_DOZING_DURATION
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -388,7 +390,9 @@ constructor(
companion object {
const val TAG = "FromLockscreenTransitionInteractor"
private val DEFAULT_DURATION = 400.milliseconds
+ val TO_DOZING_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
+ val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
val TO_AOD_DURATION = 500.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 706aba3c0505..f7d1543e4650 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -72,6 +72,10 @@ constructor(
val fromDreamingTransition: Flow<TransitionStep> =
repository.transitions.filter { step -> step.from == DREAMING }
+ /** LOCKSCREEN->(any) transition information. */
+ val fromLockscreenTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.from == LOCKSCREEN }
+
/** (any)->Lockscreen transition information */
val anyStateToLockscreenTransition: Flow<TransitionStep> =
repository.transitions.filter { step -> step.to == LOCKSCREEN }
@@ -113,9 +117,16 @@ constructor(
val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
+ /** GONE->LOCKSCREEN transition information. */
+ val goneToLockscreenTransition: Flow<TransitionStep> = repository.transition(GONE, LOCKSCREEN)
+
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
+ /** LOCKSCREEN->DOZING transition information. */
+ val lockscreenToDozingTransition: Flow<TransitionStep> =
+ repository.transition(LOCKSCREEN, DOZING)
+
/** LOCKSCREEN->DREAMING transition information. */
val lockscreenToDreamingTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, DREAMING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 99025acef70d..abd79ab793d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,9 +30,7 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.common.shared.model.Icon
@@ -40,6 +38,7 @@ import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.flow.Flow
@@ -48,9 +47,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
-/**
- * This is only for a SINGLE Quick affordance
- */
+/** This is only for a SINGLE Quick affordance */
object KeyguardQuickAffordanceViewBinder {
private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
@@ -135,28 +132,12 @@ object KeyguardQuickAffordanceViewBinder {
vibratorHelper: VibratorHelper?,
) {
if (!viewModel.isVisible) {
- view.alpha = 1f
- view
- .animate()
- .alpha(0f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
- .withEndAction { view.isInvisible = true }
- .start()
+ view.isInvisible = true
return
}
if (!view.isVisible) {
view.isVisible = true
- if (viewModel.animateReveal) {
- view.alpha = 0f
- view
- .animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setDuration(EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS)
- .start()
- }
}
IconViewBinder.bind(viewModel.icon, view)
@@ -216,13 +197,14 @@ object KeyguardQuickAffordanceViewBinder {
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
if (viewModel.useLongPress) {
- val onTouchListener = KeyguardQuickAffordanceOnTouchListener(
- view,
- viewModel,
- messageDisplayer,
- vibratorHelper,
- falsingManager,
- )
+ val onTouchListener =
+ KeyguardQuickAffordanceOnTouchListener(
+ view,
+ viewModel,
+ messageDisplayer,
+ vibratorHelper,
+ falsingManager,
+ )
view.setOnTouchListener(onTouchListener)
view.setOnClickListener {
messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
@@ -241,9 +223,7 @@ object KeyguardQuickAffordanceViewBinder {
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
- shakeAnimator.doOnEnd {
- view.translationX = 0f
- }
+ shakeAnimator.doOnEnd { view.translationX = 0f }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
@@ -268,18 +248,18 @@ object KeyguardQuickAffordanceViewBinder {
alphaFlow: Flow<Float>,
) {
combine(viewModel.map { it.isDimmed }, alphaFlow) { isDimmed, alpha ->
- if (isDimmed) DIM_ALPHA else alpha
- }
+ if (isDimmed) DIM_ALPHA else alpha
+ }
.collect { view.alpha = it }
}
private fun loadFromResources(view: View): ConfigurationBasedDimensions {
return ConfigurationBasedDimensions(
buttonSizePx =
- Size(
- view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
- view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
- ),
+ Size(
+ view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
+ view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+ ),
)
}
@@ -337,11 +317,9 @@ object KeyguardQuickAffordanceViewBinder {
}
override fun onLongClickUseDefaultHapticFeedback(view: View) = false
-
}
private data class ConfigurationBasedDimensions(
val buttonSizePx: Size,
)
-
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a2e930c49511..59c798bfca1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -84,6 +84,7 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
/** Renders the preview of the lock screen. */
@@ -158,7 +159,6 @@ constructor(
init {
if (keyguardBottomAreaRefactor()) {
- keyguardRootViewModel.enablePreviewMode()
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
bundle.getString(
@@ -287,6 +287,10 @@ constructor(
return
}
+ if (smartSpaceView != null) {
+ parentView.removeView(smartSpaceView)
+ }
+
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
val topPadding: Int =
@@ -334,26 +338,27 @@ constructor(
),
)
}
-
@OptIn(ExperimentalCoroutinesApi::class)
private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
val keyguardRootView = KeyguardRootView(previewContext, null)
- disposables.add(
- KeyguardRootViewBinder.bind(
- keyguardRootView,
- keyguardRootViewModel,
- configuration,
- featureFlags,
- occludingAppDeviceEntryMessageViewModel,
- chipbarCoordinator,
- screenOffAnimationController,
- shadeInteractor,
- null, // clock provider only needed for burn in
- null, // jank monitor not required for preview mode
- null, // device entry haptics not required for preview mode
- null, // device entry haptics not required for preview mode
+ if (!keyguardBottomAreaRefactor()) {
+ disposables.add(
+ KeyguardRootViewBinder.bind(
+ keyguardRootView,
+ keyguardRootViewModel,
+ configuration,
+ featureFlags,
+ occludingAppDeviceEntryMessageViewModel,
+ chipbarCoordinator,
+ screenOffAnimationController,
+ shadeInteractor,
+ null, // clock provider only needed for burn in
+ null, // jank monitor not required for preview mode
+ null, // device entry haptics not required preview mode
+ null, // device entry haptics not required for preview mode
+ )
)
- )
+ }
rootView.addView(
keyguardRootView,
FrameLayout.LayoutParams(
@@ -362,12 +367,13 @@ constructor(
),
)
+ setUpUdfps(previewContext, rootView)
+
disposables.add(
PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
if (keyguardBottomAreaRefactor()) {
setupShortcuts(keyguardRootView)
}
- setUpUdfps(previewContext, rootView)
if (!shouldHideClock) {
setUpClock(previewContext, rootView)
@@ -387,30 +393,30 @@ constructor(
}
private fun setupShortcuts(keyguardRootView: ConstraintLayout) {
- keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let {
+ keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView ->
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- it,
- quickAffordancesCombinedViewModel.startButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
+ view = imageView,
+ viewModel = quickAffordancesCombinedViewModel.startButton,
+ alpha = flowOf(1f),
+ falsingManager = falsingManager,
+ vibratorHelper = vibratorHelper,
+ ) { message ->
+ indicationController.showTransientIndication(message)
}
)
}
- keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let {
+ keyguardRootView.findViewById<LaunchableImageView?>(R.id.end_button)?.let { imageView ->
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- it,
- quickAffordancesCombinedViewModel.endButton,
- keyguardRootViewModel.alpha,
- falsingManager,
- vibratorHelper,
- ) {
- indicationController.showTransientIndication(it)
+ view = imageView,
+ viewModel = quickAffordancesCombinedViewModel.endButton,
+ alpha = flowOf(1f),
+ falsingManager = falsingManager,
+ vibratorHelper = vibratorHelper,
+ ) { message ->
+ indicationController.showTransientIndication(message)
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 55df46679f6d..cd46d6cf2188 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -61,7 +61,7 @@ constructor(
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
keyguardQuickAffordancesCombinedViewModel.startButton,
- keyguardRootViewModel.alpha,
+ keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
) {
@@ -71,7 +71,7 @@ constructor(
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.end_button),
keyguardQuickAffordancesCombinedViewModel.endButton,
- keyguardRootViewModel.alpha,
+ keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 0f6a966aad2e..2a68f26d3ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -25,14 +25,12 @@ import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
@@ -61,7 +59,7 @@ constructor(
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
keyguardQuickAffordancesCombinedViewModel.startButton,
- keyguardRootViewModel.alpha,
+ keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
) {
@@ -71,7 +69,7 @@ constructor(
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.end_button),
keyguardQuickAffordancesCombinedViewModel.endButton,
- keyguardRootViewModel.alpha,
+ keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 14de01b41867..1864437a7d11 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -55,6 +55,14 @@ constructor(
onStep = { 1f },
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 167.milliseconds,
+ startTime = 67.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+
val deviceEntryBackgroundViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
if (isUdfps) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 27fb8a3d2473..a728a2810916 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -41,6 +42,13 @@ constructor(
transitionFlow = interactor.dozingToLockscreenTransition,
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 150.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
new file mode 100644
index 000000000000..58235ae02abe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class DreamingHostedToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_LOCKSCREEN_DURATION,
+ transitionFlow = interactor.dreamingLockscreenHostedToLockscreenTransition
+ )
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index a3b8b85fc53d..f943bdfa7550 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -96,6 +96,14 @@ constructor(
onStep = { it },
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ startTime = 233.milliseconds,
+ duration = 250.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+
val deviceEntryBackgroundViewAlpha =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
if (isUdfps) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
new file mode 100644
index 000000000000..5804a205445c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class GoneToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_LOCKSCREEN_DURATION,
+ transitionFlow = interactor.goneToLockscreenTransition
+ )
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 02ea5508f34f..188be244be4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceIn
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -32,6 +33,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancesCombinedViewModel
@@ -39,6 +41,22 @@ class KeyguardQuickAffordancesCombinedViewModel
constructor(
private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
private val keyguardInteractor: KeyguardInteractor,
+ shadeInteractor: ShadeInteractor,
+ aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+ dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
+ dreamingHostedToLockscreenTransitionViewModel: DreamingHostedToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+ goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
+ occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+ offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel,
+ primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+ lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+ lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
+ lockscreenToDreamingHostedTransitionViewModel: LockscreenToDreamingHostedTransitionViewModel,
+ lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
+ lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel,
+ lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
+ lockscreenToPrimaryBouncerTransitionViewModel: LockscreenToPrimaryBouncerTransitionViewModel,
) {
data class PreviewMode(
@@ -60,6 +78,39 @@ constructor(
private val selectedPreviewSlotId =
MutableStateFlow(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+ /** alpha while fading the quick affordances out */
+ private val fadeInAlpha: Flow<Float> =
+ merge(
+ aodToLockscreenTransitionViewModel.shortcutsAlpha,
+ dozingToLockscreenTransitionViewModel.shortcutsAlpha,
+ dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha,
+ dreamingToLockscreenTransitionViewModel.shortcutsAlpha,
+ goneToLockscreenTransitionViewModel.shortcutsAlpha,
+ occludedToLockscreenTransitionViewModel.shortcutsAlpha,
+ offToLockscreenTransitionViewModel.shortcutsAlpha,
+ primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha,
+ )
+
+ /** alpha while fading the quick affordances in */
+ private val fadeOutAlpha: Flow<Float> =
+ merge(
+ lockscreenToAodTransitionViewModel.shortcutsAlpha,
+ lockscreenToDozingTransitionViewModel.shortcutsAlpha,
+ lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha,
+ lockscreenToDreamingTransitionViewModel.shortcutsAlpha,
+ lockscreenToGoneTransitionViewModel.shortcutsAlpha,
+ lockscreenToOccludedTransitionViewModel.shortcutsAlpha,
+ lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha,
+ shadeInteractor.qsExpansion.map { 1 - it },
+ )
+
+ /** The source of truth of alpha for all of the quick affordances on lockscreen */
+ val transitionAlpha: Flow<Float> =
+ merge(
+ fadeInAlpha,
+ fadeOutAlpha,
+ )
+
/**
* Whether quick affordances are "opaque enough" to be considered visible to and interactive by
* the user. If they are not interactive, user input should not be allowed on them.
@@ -73,7 +124,7 @@ constructor(
* interactive/clickable unless "fully opaque" to avoid issues like in b/241830987.
*/
private val areQuickAffordancesFullyOpaque: Flow<Boolean> =
- keyguardInteractor.keyguardAlpha
+ transitionAlpha
.map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD }
.distinctUntilChanged()
@@ -89,7 +140,7 @@ constructor(
* Notifies that a slot with the given ID has been selected in the preview experience that is
* rendering in the wallpaper picker. This is ignored for the real lock screen experience.
*
- * @see [KeyguardRootViewModel.enablePreviewMode]
+ * @see [enablePreviewMode]
*/
fun onPreviewSlotSelected(slotId: String) {
selectedPreviewSlotId.value = slotId
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 524fa1ede90a..f63afebb60ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -47,13 +47,11 @@ import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -74,16 +72,6 @@ constructor(
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
screenOffAnimationController: ScreenOffAnimationController,
) {
-
- data class PreviewMode(val isInPreviewMode: Boolean = false)
-
- /**
- * Whether this view-model instance is powering the preview experience that renders exclusively
- * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
- * experience.
- */
- private val previewMode = MutableStateFlow(PreviewMode())
-
var clockControllerProvider: Provider<ClockController>? = null
/** System insets that keyguard needs to stay out of */
@@ -103,14 +91,7 @@ constructor(
keyguardInteractor.notificationContainerBounds
/** An observable for the alpha level for the entire keyguard root view. */
- val alpha: Flow<Float> =
- previewMode.flatMapLatest {
- if (it.isInPreviewMode) {
- flowOf(1f)
- } else {
- keyguardInteractor.keyguardAlpha.distinctUntilChanged()
- }
- }
+ val alpha: Flow<Float> = keyguardInteractor.keyguardAlpha.distinctUntilChanged()
private fun burnIn(): Flow<BurnInModel> {
val dozingAmount: Flow<Float> =
@@ -147,55 +128,29 @@ constructor(
val lockscreenStateAlpha: Flow<Float> = aodToLockscreenTransitionViewModel.lockscreenAlpha
/** For elements that appear and move during the animation -> AOD */
- val burnInLayerAlpha: Flow<Float> =
- previewMode.flatMapLatest {
- if (it.isInPreviewMode) {
- flowOf(1f)
- } else {
- goneToAodTransitionViewModel.enterFromTopAnimationAlpha
- }
- }
+ val burnInLayerAlpha: Flow<Float> = goneToAodTransitionViewModel.enterFromTopAnimationAlpha
val translationY: Flow<Float> =
- previewMode.flatMapLatest {
- if (it.isInPreviewMode) {
- flowOf(0f)
- } else {
- keyguardInteractor.configurationChange.flatMapLatest { _ ->
- val enterFromTopAmount =
- context.resources.getDimensionPixelSize(
- R.dimen.keyguard_enter_from_top_translation_y
- )
- combine(
- keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
- burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
- goneToAodTransitionViewModel
- .enterFromTopTranslationY(enterFromTopAmount)
- .onStart { emit(0f) },
- ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY ->
- // All 3 values need to be combined for a smooth translation
- keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY
- }
- }
+ keyguardInteractor.configurationChange.flatMapLatest { _ ->
+ val enterFromTopAmount =
+ context.resources.getDimensionPixelSize(
+ R.dimen.keyguard_enter_from_top_translation_y
+ )
+ combine(
+ keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
+ burnIn().map { it.translationY.toFloat() }.onStart { emit(0f) },
+ goneToAodTransitionViewModel.enterFromTopTranslationY(enterFromTopAmount).onStart {
+ emit(0f)
+ },
+ ) { keyguardTransitionY, burnInTranslationY, goneToAodTransitionTranslationY ->
+ // All 3 values need to be combined for a smooth translation
+ keyguardTransitionY + burnInTranslationY + goneToAodTransitionTranslationY
}
}
- val translationX: Flow<Float> =
- previewMode.flatMapLatest {
- if (it.isInPreviewMode) {
- flowOf(0f)
- } else {
- burnIn().map { it.translationX.toFloat() }
- }
- }
+ val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() }
- val scale: Flow<Pair<Float, Boolean>> =
- previewMode.flatMapLatest { previewMode ->
- burnIn().map {
- val scale = if (previewMode.isInPreviewMode) 1f else it.scale
- Pair(scale, it.scaleClockOnly)
- }
- }
+ val scale: Flow<Pair<Float, Boolean>> = burnIn().map { Pair(it.scale, it.scaleClockOnly) }
/** Is the notification icon container visible? */
val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
@@ -238,20 +193,7 @@ constructor(
}
.distinctUntilChanged()
- /**
- * Puts this view-model in "preview mode", which means it's being used for UI that is rendering
- * the lock screen preview in wallpaper picker / settings and not the real experience on the
- * lock screen.
- */
- fun enablePreviewMode() {
- previewMode.value = PreviewMode(true)
- }
-
fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) {
- // Notifications should not be visible in preview mode
- if (previewMode.value.isInPreviewMode) {
- return
- }
keyguardInteractor.setNotificationContainerBounds(NotificationContainerBounds(top, bottom))
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 2bf12e8e33b2..8e8fd75cc1c0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -57,6 +57,15 @@ constructor(
onFinish = { 0f },
),
)
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
isUdfpsEnrolledAndEnabled ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
new file mode 100644
index 000000000000..263ed11503ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class LockscreenToDozingTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_DOZING_DURATION,
+ transitionFlow = interactor.lockscreenToDozingTransition
+ )
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
new file mode 100644
index 000000000000..17015056bda0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class LockscreenToDreamingHostedTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_DREAMING_HOSTED_DURATION,
+ transitionFlow = interactor.lockscreenToDreamingLockscreenHostedTransition
+ )
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 52296137a3d6..401c0ff76c29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -62,6 +62,14 @@ constructor(
onStep = { 1f - it },
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+
override val deviceEntryParentViewAlpha: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsNotExpanded = lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 59e5aa845051..cfb4bf59c8a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -23,6 +23,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -43,6 +44,14 @@ constructor(
transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index d49bc4994b0f..a6136f95d0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -50,6 +50,14 @@ constructor(
onStep = { 1f - it },
)
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ )
+
/** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
return transitionAnimation.createFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index f04b67a1d4d4..07dd4ef49c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -26,6 +26,7 @@ import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
/**
* Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -46,6 +47,11 @@ constructor(
interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
)
+ val shortcutsAlpha: Flow<Float> =
+ interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER).map {
+ 1 - it.value
+ }
+
override val deviceEntryParentViewAlpha: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsNotExpanded =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 0bdc85d05106..58be0934beca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -58,6 +58,13 @@ constructor(
)
}
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+
/** Lockscreen views alpha */
val lockscreenAlpha: Flow<Float> =
transitionAnimation.createFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
new file mode 100644
index 000000000000..c3bc799435a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class OffToLockscreenTransitionViewModel
+@Inject
+constructor(
+ interactor: KeyguardTransitionInteractor,
+) {
+
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = 250.milliseconds,
+ transitionFlow = interactor.offToLockscreenTransition
+ )
+
+ val shortcutsAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 250.milliseconds,
+ onStep = { it },
+ onCancel = { 0f },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 3cf793ab9dc8..7ef8374023fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -28,6 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
/**
* Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -57,6 +58,11 @@ constructor(
}
}
+ val shortcutsAlpha: Flow<Float> =
+ interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN).map {
+ it.value
+ }
+
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index eba1c25d0e74..388418487725 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -36,11 +36,11 @@ import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -64,6 +64,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private static final String TAG = "QSAnimator";
+ private static final int ANIMATORS_UPDATE_DELAY_MS = 100;
private static final float EXPANDED_TILE_DELAY = .86f;
//Non first page delays
private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f;
@@ -133,7 +134,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private int mLastQQSTileHeight;
private float mLastPosition;
private final QSHost mHost;
- private final Executor mExecutor;
+ private final DelayableExecutor mExecutor;
private boolean mShowCollapsedOnKeyguard;
private int mQQSTop;
@@ -144,7 +145,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
- @Main Executor executor, TunerService tunerService,
+ @Main DelayableExecutor executor, TunerService tunerService,
QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQsRootView = rootView;
mQuickQsPanel = quickPanel;
@@ -753,7 +754,10 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
public void onTilesChanged() {
// Give the QS panels a moment to generate their new tiles, then create all new animators
// hooked up to the new views.
- mExecutor.execute(mUpdateAnimators);
+ mExecutor.executeDelayed(mUpdateAnimators, ANIMATORS_UPDATE_DELAY_MS);
+
+ // Also requests a lazy animators update in case the animation starts before the executor.
+ requestAnimatorUpdate();
}
private final TouchAnimator.Listener mNonFirstPageListener =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 11db69b69f13..6c930b1f3d17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -255,6 +255,10 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
for (QSTile tile : tiles) {
addTile(tile, collapsedView);
}
+ } else {
+ for (QSPanelControllerBase.TileRecord record : mRecords) {
+ record.tile.addCallback(record.callback);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
index a321eef75a14..6f5dea32bd83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -18,17 +18,19 @@ package com.android.systemui.qs.external
import android.content.ComponentName
import android.content.Context
+import android.content.SharedPreferences
import android.service.quicksettings.Tile
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
+import javax.inject.Inject
import org.json.JSONException
import org.json.JSONObject
-import javax.inject.Inject
data class TileServiceKey(val componentName: ComponentName, val user: Int) {
private val string = "${componentName.flattenToString()}:$user"
override fun toString() = string
}
+
private const val STATE = "state"
private const val LABEL = "label"
private const val SUBTITLE = "subtitle"
@@ -44,12 +46,7 @@ private const val STATE_DESCRIPTION = "state_description"
* It persists the state from a [Tile] necessary to present the view in the same state when
* retrieved, with the exception of the icon.
*/
-class CustomTileStatePersister @Inject constructor(context: Context) {
- companion object {
- private const val FILE_NAME = "custom_tiles_state"
- }
-
- private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+interface CustomTileStatePersister {
/**
* Read the state from [SharedPreferences].
@@ -58,7 +55,31 @@ class CustomTileStatePersister @Inject constructor(context: Context) {
*
* Any fields that have not been saved will be set to `null`
*/
- fun readState(key: TileServiceKey): Tile? {
+ fun readState(key: TileServiceKey): Tile?
+ /**
+ * Persists the state into [SharedPreferences].
+ *
+ * The implementation does not store fields that are `null` or icons.
+ */
+ fun persistState(key: TileServiceKey, tile: Tile)
+ /**
+ * Removes the state for a given tile, user pair.
+ *
+ * Used when the tile is removed by the user.
+ */
+ fun removeState(key: TileServiceKey)
+}
+
+// TODO(b/299909989) Merge this class into into CustomTileRepository
+class CustomTileStatePersisterImpl @Inject constructor(context: Context) :
+ CustomTileStatePersister {
+ companion object {
+ private const val FILE_NAME = "custom_tiles_state"
+ }
+
+ private val sharedPreferences: SharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+
+ override fun readState(key: TileServiceKey): Tile? {
val state = sharedPreferences.getString(key.toString(), null) ?: return null
return try {
readTileFromString(state)
@@ -68,23 +89,13 @@ class CustomTileStatePersister @Inject constructor(context: Context) {
}
}
- /**
- * Persists the state into [SharedPreferences].
- *
- * The implementation does not store fields that are `null` or icons.
- */
- fun persistState(key: TileServiceKey, tile: Tile) {
+ override fun persistState(key: TileServiceKey, tile: Tile) {
val state = writeToString(tile)
sharedPreferences.edit().putString(key.toString(), state).apply()
}
- /**
- * Removes the state for a given tile, user pair.
- *
- * Used when the tile is removed by the user.
- */
- fun removeState(key: TileServiceKey) {
+ override fun removeState(key: TileServiceKey) {
sharedPreferences.edit().remove(key.toString()).apply()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 69fe46aa9009..529d68407ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -109,7 +109,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
// Only read and modified in main thread (where click events come through).
private int mClickEventId = 0;
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final ArraySet<Callback> mCallbacks = new ArraySet<>();
private final Object mStaleListener = new Object();
protected TState mState;
private TState mTmpState;
@@ -444,9 +444,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
}
private void handleStateChanged() {
- if (mCallbacks.size() != 0) {
+ if (!mCallbacks.isEmpty()) {
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onStateChanged(mState);
+ mCallbacks.valueAt(i).onStateChanged(mState);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 94137c88098e..4a34276671c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.di
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.CustomTileStatePersisterImpl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
@@ -52,4 +54,7 @@ interface QSTilesModule {
fun bindQSTileIntentUserInputHandler(
impl: QSTileIntentUserInputHandlerImpl
): QSTileIntentUserInputHandler
+
+ @Binds
+ fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 5bdb592a3558..db3cf0f70f69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -81,10 +81,8 @@ constructor(
private lateinit var toggleView: Switch
private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
- private lateinit var seeAllViewGroup: View
- private lateinit var pairNewDeviceViewGroup: View
- private lateinit var seeAllRow: View
- private lateinit var pairNewDeviceRow: View
+ private lateinit var seeAllButton: View
+ private lateinit var pairNewDeviceButton: View
private lateinit var deviceListView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
@@ -99,10 +97,8 @@ constructor(
toggleView = requireViewById(R.id.bluetooth_toggle)
subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
doneButton = requireViewById(R.id.done_button)
- seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
- pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
- seeAllRow = requireViewById(R.id.see_all_clickable_row)
- pairNewDeviceRow = requireViewById(R.id.pair_new_device_clickable_row)
+ seeAllButton = requireViewById(R.id.see_all_button)
+ pairNewDeviceButton = requireViewById(R.id.pair_new_device_button)
deviceListView = requireViewById<RecyclerView>(R.id.device_list)
setupToggle()
@@ -110,8 +106,8 @@ constructor(
subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
- seeAllRow.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceRow.setOnClickListener {
+ seeAllButton.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceButton.setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
}
@@ -134,8 +130,8 @@ constructor(
}
if (isActive) {
deviceItemAdapter.refreshDeviceItemList(deviceItem) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ seeAllButton.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceButton.visibility = if (showPairNewDevice) VISIBLE else GONE
lastUiUpdateMs = systemClock.elapsedRealtime()
lastItemRow = itemRow
logger.logDeviceUiUpdate(lastUiUpdateMs - start)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 34c2aba1a71f..5d5e747ba979 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -20,7 +20,6 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogCuj
@@ -40,6 +39,8 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -63,26 +64,25 @@ constructor(
private var job: Job? = null
- @VisibleForTesting internal var dialog: BluetoothTileDialog? = null
-
/**
* Shows the dialog.
*
* @param context The context in which the dialog is displayed.
* @param view The view from which the dialog is shown.
*/
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
fun showDialog(context: Context, view: View?) {
- dismissDialog()
-
- var updateDeviceItemJob: Job? = null
- var updateDialogUiJob: Job? = null
+ cancelJob()
job =
coroutineScope.launch(mainDispatcher) {
- dialog = createBluetoothTileDialog(context)
+ var updateDeviceItemJob: Job?
+ var updateDialogUiJob: Job? = null
+ val dialog = createBluetoothTileDialog(context)
+
view?.let {
dialogLaunchAnimator.showFromView(
- dialog!!,
+ dialog,
it,
animateBackgroundBoundsChange = true,
cuj =
@@ -92,9 +92,8 @@ constructor(
)
)
}
- ?: dialog!!.show()
+ ?: dialog.show()
- updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
@@ -102,7 +101,7 @@ constructor(
bluetoothStateInteractor.bluetoothStateUpdate
.filterNotNull()
.onEach {
- dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it))
+ dialog.onBluetoothStateUpdated(it, getSubtitleResId(it))
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(
@@ -129,7 +128,7 @@ constructor(
.onEach {
updateDialogUiJob?.cancel()
updateDialogUiJob = launch {
- dialog?.onDeviceItemUpdated(
+ dialog.onDeviceItemUpdated(
it.take(MAX_DEVICE_ITEM_ENTRY),
showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
@@ -138,15 +137,15 @@ constructor(
}
.launchIn(this)
- dialog!!
- .bluetoothStateToggle
+ dialog.bluetoothStateToggle
.onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
.launchIn(this)
- dialog!!
- .deviceItemClick
+ dialog.deviceItemClick
.onEach { deviceItemInteractor.updateDeviceItemOnClick(it) }
.launchIn(this)
+
+ produce<Unit> { awaitClose { dialog.cancel() } }
}
}
@@ -161,7 +160,7 @@ constructor(
logger,
context
)
- .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } }
+ .apply { SystemUIDialog.registerDismissListener(this) { cancelJob() } }
}
override fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) {
@@ -188,15 +187,13 @@ constructor(
startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
}
- private fun dismissDialog() {
+ private fun cancelJob() {
job?.cancel()
job = null
- dialog?.dismiss()
- dialog = null
}
private fun startSettingsActivity(intent: Intent, view: View) {
- dialog?.run {
+ if (job?.isActive == true) {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
activityStarter.postStartActivityDismissingKeyguard(
intent,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index 76fbf8e427e7..fcd45a6431bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -168,26 +168,30 @@ constructor(
)
}
- internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
- logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
-
- deviceItem.cachedBluetoothDevice.apply {
- when (deviceItem.type) {
- DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
- }
- DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
- setActive()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
- }
- DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT)
- }
- DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
- connect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ internal suspend fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
+ withContext(backgroundDispatcher) {
+ logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+
+ deviceItem.cachedBluetoothDevice.apply {
+ when (deviceItem.type) {
+ DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
+ }
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+ setActive()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
+ }
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(
+ BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT
+ )
+ }
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
+ connect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index c390695b1911..cfb544226c83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.airplane.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,29 +27,27 @@ import com.android.systemui.res.R
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(private val context: Context) :
+class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- }
- )!!,
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
new file mode 100644
index 000000000000..869f6f321d21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.commons
+
+import android.service.quicksettings.Tile
+
+fun Tile.copy(): Tile =
+ Tile().also {
+ it.icon = icon
+ it.label = label
+ it.subtitle = subtitle
+ it.contentDescription = contentDescription
+ it.stateDescription = stateDescription
+ it.activityLaunchForClick = activityLaunchForClick
+ it.state = state
+ }
+
+fun Tile.setFrom(otherTile: Tile) {
+ if (otherTile.icon != null) {
+ icon = otherTile.icon
+ }
+ if (otherTile.customLabel != null) {
+ label = otherTile.customLabel
+ }
+ if (otherTile.subtitle != null) {
+ subtitle = otherTile.subtitle
+ }
+ if (otherTile.contentDescription != null) {
+ contentDescription = otherTile.contentDescription
+ }
+ if (otherTile.stateDescription != null) {
+ stateDescription = otherTile.stateDescription
+ }
+ activityLaunchForClick = otherTile.activityLaunchForClick
+ state = otherTile.state
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
new file mode 100644
index 000000000000..ca5302e13545
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.commons.setFrom
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository store the [Tile] associated with the custom tile. It lives on [QSTileScope] which
+ * allows it to survive service rebinding. Given that, it provides the last received state when
+ * connected again.
+ */
+interface CustomTileRepository {
+
+ /**
+ * Restores the [Tile] if it's [isPersistable]. Restored [Tile] will be available via [getTile]
+ * (but there is no guarantee that restoration is synchronous) and emitted in [getTiles] for a
+ * corresponding [user].
+ */
+ suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean)
+
+ /** Returns [Tile] updates for a [user]. */
+ fun getTiles(user: UserHandle): Flow<Tile>
+
+ /**
+ * Return current [Tile] for a [user] or null if the [user] doesn't match currently cached one.
+ * Suspending until [getTiles] returns something is a way to wait for this to become available.
+ *
+ * @throws IllegalStateException when there is no current tile.
+ */
+ fun getTile(user: UserHandle): Tile?
+
+ /**
+ * Updates tile with the non-null values from [newTile]. Overwrites the current cache when
+ * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
+ * loaded when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ )
+
+ /**
+ * Updates tile with the values from [defaults]. Overwrites the current cache when [user]
+ * differs from the cached one. [isPersistable] tile will be persisted to be possibly loaded
+ * when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ )
+}
+
+@QSTileScope
+class CustomTileRepositoryImpl
+@Inject
+constructor(
+ private val tileSpec: TileSpec.CustomTileSpec,
+ private val customTileStatePersister: CustomTileStatePersister,
+ @Background private val backgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val tileUpdateMutex = Mutex()
+ private val tileWithUserState =
+ MutableSharedFlow<TileWithUser>(onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1)
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) {
+ if (isPersistable && getCurrentTileWithUser()?.user != user) {
+ withContext(backgroundContext) {
+ customTileStatePersister.readState(user.getKey())?.let {
+ updateWithTile(
+ user,
+ it,
+ true,
+ )
+ }
+ }
+ }
+ }
+
+ override fun getTiles(user: UserHandle): Flow<Tile> =
+ tileWithUserState.filter { it.user == user }.map { it.tile }
+
+ override fun getTile(user: UserHandle): Tile? {
+ val tileWithUser =
+ getCurrentTileWithUser() ?: throw IllegalStateException("Tile is not set")
+ return if (tileWithUser.user == user) {
+ tileWithUser.tile
+ } else {
+ null
+ }
+ }
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = updateTile(user, isPersistable) { setFrom(newTile) }
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) {
+ if (defaults is CustomTileDefaults.Result) {
+ updateTile(user, isPersistable) {
+ // Update the icon if it's not set or is the default icon.
+ val updateIcon = (icon == null || icon.isResourceEqual(defaults.icon))
+ if (updateIcon) {
+ icon = defaults.icon
+ }
+ setDefaultLabel(defaults.label)
+ }
+ }
+ }
+
+ private suspend fun updateTile(
+ user: UserHandle,
+ isPersistable: Boolean,
+ update: Tile.() -> Unit
+ ): Unit =
+ tileUpdateMutex.withLock {
+ val currentTileWithUser = getCurrentTileWithUser()
+ val tileToUpdate =
+ if (currentTileWithUser?.user == user) {
+ currentTileWithUser.tile.copy()
+ } else {
+ Tile()
+ }
+ tileToUpdate.update()
+ if (isPersistable) {
+ withContext(backgroundContext) {
+ customTileStatePersister.persistState(user.getKey(), tileToUpdate)
+ }
+ }
+ tileWithUserState.tryEmit(TileWithUser(user, tileToUpdate))
+ }
+
+ private fun getCurrentTileWithUser(): TileWithUser? = tileWithUserState.replayCache.lastOrNull()
+
+ /** Compare two icons, only works for resources. */
+ private fun Icon.isResourceEqual(icon2: Icon?): Boolean {
+ if (icon2 == null) {
+ return false
+ }
+ if (this === icon2) {
+ return true
+ }
+ if (type != Icon.TYPE_RESOURCE || icon2.type != Icon.TYPE_RESOURCE) {
+ return false
+ }
+ if (resId != icon2.resId) {
+ return false
+ }
+ return resPackage == icon2.resPackage
+ }
+
+ private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier)
+
+ private data class TileWithUser(val user: UserHandle, val tile: Tile)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 83767aa9d444..d956fdebcd32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -24,6 +24,8 @@ import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
@@ -50,4 +52,6 @@ interface CustomTileModule {
fun bindCustomTileDefaultsRepository(
impl: CustomTileDefaultsRepositoryImpl
): CustomTileDefaultsRepository
+
+ @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
new file mode 100644
index 000000000000..351bba538463
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/** Manages updates of the [Tile] assigned for the current custom tile. */
+@CustomTileBoundScope
+class CustomTileInteractor
+@Inject
+constructor(
+ private val user: UserHandle,
+ private val defaultsRepository: CustomTileDefaultsRepository,
+ private val customTileRepository: CustomTileRepository,
+ private val tileServiceManager: TileServiceManager,
+ @CustomTileBoundScope private val boundScope: CoroutineScope,
+ @Background private val backgroundContext: CoroutineContext,
+) {
+
+ private val tileUpdates =
+ MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ /** [Tile] updates. [updateTile] to emit a new one. */
+ val tiles: Flow<Tile>
+ get() = customTileRepository.getTiles(user)
+
+ /**
+ * Current [Tile]
+ *
+ * @throws IllegalStateException when the repository stores a tile for another user. This means
+ * the tile hasn't been updated for the current user. Can happen when this is accessed before
+ * [init] returns.
+ */
+ val tile: Tile
+ get() =
+ customTileRepository.getTile(user)
+ ?: throw IllegalStateException("Attempt to get a tile for a wrong user")
+
+ /**
+ * Initializes the repository for the current user. Suspends until it's safe to call [tile]
+ * which needs at least one of the following:
+ * - defaults are loaded;
+ * - receive tile update in [updateTile];
+ * - restoration happened for a persisted tile.
+ */
+ suspend fun init() {
+ launchUpdates()
+ customTileRepository.restoreForTheUserIfNeeded(user, tileServiceManager.isActiveTile)
+ // Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
+ // tile update.
+ customTileRepository.getTiles(user).firstOrNull()
+ }
+
+ private fun launchUpdates() {
+ tileUpdates
+ .onEach {
+ customTileRepository.updateWithTile(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ defaultsRepository
+ .defaults(user)
+ .onEach {
+ customTileRepository.updateWithDefaults(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ }
+
+ /** Updates current [Tile]. Emits a new event in [tiles]. */
+ fun updateTile(newTile: Tile) {
+ tileUpdates.tryEmit(newTile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index b2b226464ee5..881a6bd156d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,30 +27,28 @@ import com.android.systemui.res.R
import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(private val context: Context) :
+class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 8e53723a5a6b..7e7034d65efd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,32 +27,30 @@ import com.android.systemui.res.R
import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(private val context: Context) :
+class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
- this.label = context.resources.getString(R.string.quick_settings_location_label)
+ this.label = resources.getString(R.string.quick_settings_location_label)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index f9e0b160acd6..23e0cb66bb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
+import android.content.res.Resources
import android.service.quicksettings.Tile
import android.view.View
import android.widget.Switch
@@ -46,13 +46,13 @@ data class QSTileState(
companion object {
fun build(
- context: Context,
+ resources: Resources,
config: QSTileUIConfig,
build: Builder.() -> Unit
): QSTileState =
build(
{ Icon.Resource(config.iconRes, null) },
- context.getString(config.labelRes),
+ resources.getString(config.labelRes),
build,
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index f3f9c916d705..d42fde617394 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -128,7 +128,7 @@ constructor(
private fun automaticallySwitchScenes() {
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
- bouncerInteractor.onImeHidden.collectLatest {
+ bouncerInteractor.onImeHiddenByUser.collectLatest {
if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
@@ -146,19 +146,21 @@ constructor(
isAnySimLocked -> {
switchToScene(
targetSceneKey = SceneKey.Bouncer,
- loggingReason = "Need to authenticate locked sim card."
+ loggingReason = "Need to authenticate locked SIM card."
)
}
- isUnlocked && !canSwipeToEnter -> {
+ isUnlocked && canSwipeToEnter == false -> {
switchToScene(
targetSceneKey = SceneKey.Gone,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device already" +
+ " unlocked and lockscreen doesn't require a swipe to dismiss."
)
}
else -> {
switchToScene(
targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device still locked" +
+ " or lockscreen still requires a swipe to dismiss."
)
}
}
@@ -205,11 +207,17 @@ constructor(
// when the user is passively authenticated, the false value here
// when the unlock state changes indicates this is an active
// authentication attempt.
- if (isBypassEnabled || !canSwipeToEnter)
- SceneKey.Gone to
- "device has been unlocked on lockscreen with either " +
- "bypass enabled or using an active authentication mechanism"
- else null
+ when {
+ isBypassEnabled ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with bypass" +
+ " enabled"
+ canSwipeToEnter == false ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen using an active" +
+ " authentication mechanism"
+ else -> null
+ }
// Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
@@ -232,7 +240,7 @@ constructor(
} else {
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- if (isUnlocked && !canSwipeToEnter) {
+ if (isUnlocked && canSwipeToEnter == false) {
switchToScene(
targetSceneKey = SceneKey.Gone,
loggingReason =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index d14ef35027a3..dbb58a329272 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -16,12 +16,14 @@
package com.android.systemui.scene.shared.flag
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flag
import com.android.systemui.flags.Flags
@@ -29,6 +31,7 @@ import com.android.systemui.flags.ReleasedFlag
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -51,6 +54,7 @@ interface SceneContainerFlags {
class SceneContainerFlagsImpl
@AssistedInject
constructor(
+ @Application private val context: Context,
private val featureFlagsClassic: FeatureFlagsClassic,
@Assisted private val isComposeAvailable: Boolean,
) : SceneContainerFlags {
@@ -80,7 +84,11 @@ constructor(
),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
- listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
+ listOf(
+ ComposeMustBeAvailable(),
+ CompileTimeFlagMustBeEnabled(),
+ ResourceConfigMustBeEnabled()
+ )
override fun isEnabled(): Boolean {
// SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
@@ -146,6 +154,14 @@ constructor(
}
}
+ private inner class ResourceConfigMustBeEnabled : Requirement {
+ override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"
+
+ override fun isMet(): Boolean {
+ return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index e7481ccd0efd..b98093e50920 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -32,13 +32,16 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
import android.view.Gravity;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
@@ -115,6 +118,17 @@ public class ScreenRecordDialog extends SystemUIDialog {
mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
mAudioSwitch.setChecked(true);
});
+
+ // disable redundant Touch & Hold accessibility action for Switch Access
+ mOptions.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+ mOptions.setLongClickable(false);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 9f4ea27b9ee6..d13edf01cc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -39,6 +39,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -58,18 +59,21 @@ public class BrightnessDialog extends Activity {
private final DelayableExecutor mMainExecutor;
private final AccessibilityManagerWrapper mAccessibilityMgr;
private Runnable mCancelTimeoutRunnable;
+ private final ShadeInteractor mShadeInteractor;
@Inject
public BrightnessDialog(
BrightnessSliderController.Factory brightnessSliderfactory,
BrightnessController.Factory brightnessControllerFactory,
@Main DelayableExecutor mainExecutor,
- AccessibilityManagerWrapper accessibilityMgr
+ AccessibilityManagerWrapper accessibilityMgr,
+ ShadeInteractor shadeInteractor
) {
mToggleSliderFactory = brightnessSliderfactory;
mBrightnessControllerFactory = brightnessControllerFactory;
mMainExecutor = mainExecutor;
mAccessibilityMgr = accessibilityMgr;
+ mShadeInteractor = shadeInteractor;
}
@@ -79,6 +83,10 @@ public class BrightnessDialog extends Activity {
setWindowAttributes();
setContentView(R.layout.brightness_mirror_container);
setBrightnessDialogViewAttributes();
+
+ if (mShadeInteractor.isQsExpanded().getValue()) {
+ finish();
+ }
}
private void setWindowAttributes() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 2a071def083a..0065db370b12 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -66,10 +66,10 @@ constructor(
private fun upDestinationSceneKey(
isUnlocked: Boolean,
- canSwipeToDismiss: Boolean,
+ canSwipeToDismiss: Boolean?,
): SceneKey {
return when {
- canSwipeToDismiss -> SceneKey.Lockscreen
+ canSwipeToDismiss == true -> SceneKey.Lockscreen
isUnlocked -> SceneKey.Gone
else -> SceneKey.Lockscreen
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 1b096b592a4a..c1a630f48232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -612,7 +612,7 @@ public final class KeyboardShortcutListSearch {
private static KeyboardShortcutMultiMappingGroup getMultiMappingInputShortcuts(
Context context) {
List<ShortcutMultiMappingInfo> shortcutMultiMappingInfoList = Arrays.asList(
- /* Switch input language (next language): Ctrl + Space or Meta + Space */
+ /* Switch input language (next language): Ctrl + Space */
new ShortcutMultiMappingInfo(
context.getString(R.string.input_switch_input_language_next),
null,
@@ -621,14 +621,9 @@ public final class KeyboardShortcutListSearch {
context.getString(
R.string.input_switch_input_language_next),
KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON),
- null),
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(
- R.string.input_switch_input_language_next),
- KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON),
null))),
/* Switch input language (previous language): */
- /* Ctrl + Shift + Space or Meta + Shift + Space */
+ /* Ctrl + Shift + Space */
new ShortcutMultiMappingInfo(
context.getString(R.string.input_switch_input_language_previous),
null,
@@ -638,12 +633,6 @@ public final class KeyboardShortcutListSearch {
R.string.input_switch_input_language_previous),
KeyEvent.KEYCODE_SPACE,
KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON),
- null),
- new ShortcutKeyGroup(new KeyboardShortcutInfo(
- context.getString(
- R.string.input_switch_input_language_previous),
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON),
null)))
);
return new KeyboardShortcutMultiMappingGroup(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 0e83c78edb1d..e486457b89bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -530,16 +530,12 @@ public class NotificationLockscreenUserManagerImpl implements
userHandle = mCurrentUserId;
}
if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs setting too early", new Throwable());
- updateUserShowPrivateSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+ return false;
}
if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs dpm override too early", new Throwable());
- updateDpcSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ return false;
}
return mUsersUsersAllowingPrivateNotifications.get(userHandle)
&& mUsersDpcAllowingPrivateNotifications.get(userHandle);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index e90ddf98db00..31893b402e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -15,48 +15,37 @@
package com.android.systemui.statusbar.notification.domain.interactor
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
class ActiveNotificationsInteractor
@Inject
constructor(
private val repository: ActiveNotificationListRepository,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
/** Notifications actively presented to the user in the notification stack, in order. */
val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
- repository.activeNotifications
- .map { store ->
- store.renderList.map { key ->
- val entry =
- store[key]
- ?: error(
- "Could not find notification with key $key in active notif store."
- )
- when (entry) {
- is ActiveNotificationGroupModel -> entry.summary
- is ActiveNotificationModel -> entry
- }
+ repository.activeNotifications.map { store ->
+ store.renderList.map { key ->
+ val entry =
+ store[key]
+ ?: error("Could not find notification with key $key in active notif store.")
+ when (entry) {
+ is ActiveNotificationGroupModel -> entry.summary
+ is ActiveNotificationModel -> entry
}
}
- .flowOn(backgroundDispatcher)
+ }
/** Are any notifications being actively presented in the notification stack? */
val areAnyNotificationsPresent: Flow<Boolean> =
- repository.activeNotifications
- .map { it.renderList.isNotEmpty() }
- .distinctUntilChanged()
- .flowOn(backgroundDispatcher)
+ repository.activeNotifications.map { it.renderList.isNotEmpty() }.distinctUntilChanged()
/**
* The same as [areAnyNotificationsPresent], but without flows, for easy access in synchronous
@@ -70,7 +59,6 @@ constructor(
repository.notifStats
.map { it.hasClearableAlertingNotifs || it.hasClearableSilentNotifs }
.distinctUntilChanged()
- .flowOn(backgroundDispatcher)
fun setNotifStats(notifStats: NotifStats) {
repository.notifStats.value = notifStats
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index 73341dbc4999..87b8e55dbd1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -15,24 +15,19 @@
*/
package com.android.systemui.statusbar.notification.domain.interactor
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOn
/** Domain logic pertaining to notifications on the keyguard. */
class NotificationsKeyguardInteractor
@Inject
constructor(
repository: NotificationsKeyguardViewStateRepository,
- @Background backgroundDispatcher: CoroutineDispatcher,
) {
/** Is a pulse expansion occurring? */
- val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding.flowOn(backgroundDispatcher)
+ val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
/** Are notifications fully hidden from view? */
- val areNotificationsFullyHidden: Flow<Boolean> =
- repository.areNotificationsFullyHidden.flowOn(backgroundDispatcher)
+ val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 64f61d9ac2da..8eda96f62257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -27,7 +27,6 @@ import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
@@ -43,11 +42,9 @@ import java.util.Arrays;
* A view that can be used for both the dimmed and normal background of an notification.
*/
public class NotificationBackgroundView extends View implements Dumpable {
- private static final String TAG = "NotificationBackgroundView";
private final boolean mDontModifyCorners;
private Drawable mBackground;
- private Drawable mBackgroundDrawableToTint;
private int mClipTopAmount;
private int mClipBottomAmount;
private int mTintColor;
@@ -134,7 +131,6 @@ public class NotificationBackgroundView extends View implements Dumpable {
unscheduleDrawable(mBackground);
}
mBackground = background;
- mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground);
mRippleColor = null;
mBackground.mutate();
if (mBackground != null) {
@@ -148,46 +144,25 @@ public class NotificationBackgroundView extends View implements Dumpable {
invalidate();
}
- // setCustomBackground should be called from ActivatableNotificationView.initBackground
- // with R.drawable.notification_material_bg, which is a layer-list with a lower layer
- // for the background color (annotated with an ID so we can find it) and an upper layer
- // to blend in the stateful @color/notification_overlay_color.
- //
- // If the notification is tinted, we want to set a tint list on *just that lower layer* that
- // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful
- // tints in the upper layer that make the hovered and pressed states visible.
- //
- // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it.
- private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) {
- if (background == null) {
- return null;
- }
-
- if (!(background instanceof LayerDrawable)) {
- Log.wtf(TAG, "background is not a LayerDrawable: " + background);
- return background;
- }
-
- final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId(
- R.id.notification_background_color_layer);
-
- if (backgroundColorLayer == null) {
- Log.wtf(TAG, "background is missing background color layer: " + background);
- return background;
- }
-
- return backgroundColorLayer;
- }
-
public void setCustomBackground(int drawableResId) {
final Drawable d = mContext.getDrawable(drawableResId);
setCustomBackground(d);
}
public void setTint(int tintColor) {
- mBackgroundDrawableToTint.setTint(tintColor);
- mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP);
-
+ if (tintColor != 0) {
+ ColorStateList stateList = new ColorStateList(new int[][]{
+ new int[]{com.android.internal.R.attr.state_pressed},
+ new int[]{com.android.internal.R.attr.state_hovered},
+ new int[]{}},
+
+ new int[]{tintColor, tintColor, tintColor}
+ );
+ mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ mBackground.setTintList(stateList);
+ } else {
+ mBackground.setTintList(null);
+ }
mTintColor = tintColor;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 6d8ec44ad55e..c615887d5c25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -67,6 +67,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
+ private boolean mIsSceneContainerVisible = false;
private boolean mShouldAdjustInsets = false;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
@@ -128,11 +129,14 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
if (sceneContainerFlags.isEnabled()) {
javaAdapter.alwaysCollectFlow(
sceneInteractor.get().isVisible(),
+ this::onSceneContainerVisibilityChanged);
+ } else {
+ javaAdapter.alwaysCollectFlow(
+ shadeInteractor.isAnyExpanded(),
this::onShadeOrQsExpanded);
}
@@ -164,6 +168,17 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
}
}
+ private void onSceneContainerVisibilityChanged(Boolean isVisible) {
+ if (isVisible != mIsSceneContainerVisible) {
+ mIsSceneContainerVisible = isVisible;
+ if (isVisible) {
+ // make sure our state is sensible
+ mForceCollapsedUntilLayout = false;
+ }
+ updateTouchableRegion();
+ }
+ }
+
/**
* Calculates the touch region needed for heads up notifications, taking into consideration
* any existing display cutouts (notch)
@@ -267,6 +282,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
// since we don't want stray touches to go through the light reveal scrim to whatever is
// underneath.
return mIsStatusBarExpanded
+ || mIsSceneContainerVisible
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index bbba19d61b5a..87df180353b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -73,7 +73,11 @@ public interface DevicePostureController extends CallbackController<Callback> {
/** Callback to be notified about device posture changes. */
interface Callback {
- /** Called when the posture changes. */
+ /**
+ * Called when the posture changes. If there are multiple active displays ("concurrent"),
+ * this will report the physical posture of the device (also known as the base device
+ * state).
+ */
void onPostureChanged(@DevicePostureInt int posture);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 8f1ac812da71..422aa4d4aa60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -39,8 +39,11 @@ import javax.inject.Inject;
/** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
@SysUISingleton
public class DevicePostureControllerImpl implements DevicePostureController {
+ /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
+ private static final int COMMON_STATE_USE_BASE_STATE = 1000;
private final List<Callback> mListeners = new ArrayList<>();
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
+ private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
@@ -73,16 +76,32 @@ public class DevicePostureControllerImpl implements DevicePostureController {
mDeviceStateToPostureMap.put(deviceState, posture);
}
- deviceStateManager.registerCallback(executor, state -> {
- Assert.isMainThread();
- mCurrentDevicePosture =
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
+ @Override
+ public void onStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentDevicePosture =
+ mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ sendUpdatePosture();
+ }
+
+ @Override
+ public void onBaseStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+
+ if (useBaseState()) {
+ sendUpdatePosture();
+ }
+ }
- ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
+ private void sendUpdatePosture() {
+ ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
l -> {
- l.onPostureChanged(mCurrentDevicePosture);
+ l.onPostureChanged(getDevicePosture());
return Unit.INSTANCE;
});
+ }
});
}
@@ -100,6 +119,14 @@ public class DevicePostureControllerImpl implements DevicePostureController {
@Override
public int getDevicePosture() {
- return mCurrentDevicePosture;
+ if (useBaseState()) {
+ return mCurrentBasePosture;
+ } else {
+ return mCurrentDevicePosture;
+ }
+ }
+
+ private boolean useBaseState() {
+ return mCurrentDevicePosture == COMMON_STATE_USE_BASE_STATE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index e576f36d573a..279e5ef1f38c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable {
@@ -24,6 +25,7 @@ public interface FlashlightController extends CallbackController<FlashlightListe
boolean isAvailable();
boolean isEnabled();
+ @WeaklyReferencedCallback
public interface FlashlightListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 8ae093a531c2..10fc83c8b82c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -20,6 +20,7 @@ import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
@@ -33,10 +34,7 @@ import java.util.Optional
import javax.inject.Named
import javax.inject.Scope
-@Scope
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class SysUIUnfoldScope
+@Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope
/**
* Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
@@ -55,20 +53,21 @@ class SysUIUnfoldModule {
@Provides
@SysUISingleton
fun provideSysUIUnfoldComponent(
- provider: Optional<UnfoldTransitionProgressProvider>,
- rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
- @Named(UNFOLD_STATUS_BAR) scopedProvider:
- Optional<ScopedUnfoldTransitionProgressProvider>,
- unfoldLatencyTracker: Lazy<UnfoldLatencyTracker>,
- factory: SysUIUnfoldComponent.Factory
+ provider: Optional<UnfoldTransitionProgressProvider>,
+ rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
+ @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ @UnfoldBg bgProvider: Optional<UnfoldTransitionProgressProvider>,
+ unfoldLatencyTracker: Lazy<UnfoldLatencyTracker>,
+ factory: SysUIUnfoldComponent.Factory
): Optional<SysUIUnfoldComponent> {
val p1 = provider.getOrNull()
val p2 = rotationProvider.getOrNull()
val p3 = scopedProvider.getOrNull()
- return if (p1 == null || p2 == null || p3 == null) {
+ val p4 = bgProvider.getOrNull()
+ return if (p1 == null || p2 == null || p3 == null || p4 == null) {
Optional.empty()
} else {
- Optional.of(factory.create(p1, p2, p3, unfoldLatencyTracker.get()))
+ Optional.of(factory.create(p1, p2, p3, p4, unfoldLatencyTracker.get()))
}
}
}
@@ -76,13 +75,15 @@ class SysUIUnfoldModule {
@SysUIUnfoldScope
@Subcomponent
interface SysUIUnfoldComponent {
+
@Subcomponent.Factory
interface Factory {
fun create(
- @BindsInstance p1: UnfoldTransitionProgressProvider,
- @BindsInstance p2: NaturalRotationUnfoldProgressProvider,
- @BindsInstance p3: ScopedUnfoldTransitionProgressProvider,
- @BindsInstance p4: UnfoldLatencyTracker,
+ @BindsInstance p1: UnfoldTransitionProgressProvider,
+ @BindsInstance p2: NaturalRotationUnfoldProgressProvider,
+ @BindsInstance p3: ScopedUnfoldTransitionProgressProvider,
+ @BindsInstance @UnfoldBg p4: UnfoldTransitionProgressProvider,
+ @BindsInstance p5: UnfoldLatencyTracker,
): SysUIUnfoldComponent
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 36a1e8a072c9..b72c6f189e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -35,6 +35,9 @@ import android.view.SurfaceControlViewHost
import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
+import com.android.app.tracing.traceSection
+import com.android.keyguard.logging.ScrimLogger
+import com.android.systemui.Flags.unfoldAnimationBackgroundProgress
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -45,16 +48,16 @@ import com.android.systemui.statusbar.LinearLightRevealEffect
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayReason.FOLD
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayReason.UNFOLD
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.concurrency.ThreadFactory
-import com.android.app.tracing.traceSection
-import com.android.keyguard.logging.ScrimLogger
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
import java.util.concurrent.Executor
import java.util.function.Consumer
import javax.inject.Inject
+import javax.inject.Provider
@SysUIUnfoldScope
class UnfoldLightRevealOverlayAnimation
@@ -65,11 +68,14 @@ constructor(
private val deviceStateManager: DeviceStateManager,
private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
- private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ @UnfoldBg
+ private val unfoldTransitionBgProgressProvider: Provider<UnfoldTransitionProgressProvider>,
+ private val unfoldTransitionProgressProvider: Provider<UnfoldTransitionProgressProvider>,
private val displayAreaHelper: Optional<DisplayAreaHelper>,
@Main private val executor: Executor,
private val threadFactory: ThreadFactory,
- private val rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg private val rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg private val unfoldProgressHandler: Handler,
private val displayTracker: DisplayTracker,
private val scrimLogger: ScrimLogger,
) {
@@ -96,11 +102,15 @@ constructor(
fun init() {
// This method will be called only on devices where this animation is enabled,
// so normally this thread won't be created
- bgHandler = threadFactory.buildHandlerOnNewThread(TAG)
+ bgHandler = unfoldProgressHandler
bgExecutor = threadFactory.buildDelayableExecutorOnHandler(bgHandler)
deviceStateManager.registerCallback(bgExecutor, FoldListener())
- unfoldTransitionProgressProvider.addCallback(transitionListener)
+ if (unfoldAnimationBackgroundProgress()) {
+ unfoldTransitionBgProgressProvider.get().addCallback(transitionListener)
+ } else {
+ unfoldTransitionProgressProvider.get().addCallback(transitionListener)
+ }
rotationChangeProvider.addCallback(rotationWatcher)
val containerBuilder =
@@ -169,8 +179,13 @@ constructor(
overlayAddReason = reason
- val newRoot = SurfaceControlViewHost(context, context.display!!, wwm,
- "UnfoldLightRevealOverlayAnimation")
+ val newRoot =
+ SurfaceControlViewHost(
+ context,
+ context.display,
+ wwm,
+ "UnfoldLightRevealOverlayAnimation"
+ )
val params = getLayoutParams()
val newView =
LightRevealScrim(
@@ -353,12 +368,13 @@ constructor(
}
private fun executeInBackground(f: () -> Unit) {
- check(Looper.myLooper() != bgHandler.looper) {
- "Trying to execute using background handler while already running" +
- " in the background handler"
+ // This is needed to allow progresses to be received both from the main thread (that will
+ // schedule a runnable on the bg thread), and from the bg thread directly (no reposting).
+ if (bgHandler.looper.isCurrentThread) {
+ f()
+ } else {
+ bgHandler.post(f)
}
- // The UiBackground executor is not used as it doesn't have a prepared looper.
- bgHandler.post(f)
}
private fun ensureInBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 12b88458d355..94912bf82377 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -21,11 +21,14 @@ import com.android.app.tracing.TraceStateLogger
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.unfold.system.DeviceStateRepository
import com.android.systemui.unfold.updates.FoldStateRepository
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.plus
/**
* Logs several unfold related details in a trace. Mainly used for debugging and investigate
@@ -37,7 +40,8 @@ class UnfoldTraceLogger
constructor(
private val context: Context,
private val foldStateRepository: FoldStateRepository,
- @Application private val applicationScope: CoroutineScope,
+ @Application applicationScope: CoroutineScope,
+ @Background private val coroutineContext: CoroutineContext,
private val deviceStateRepository: DeviceStateRepository
) : CoreStartable {
private val isFoldable: Boolean
@@ -46,20 +50,22 @@ constructor(
.getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
.isNotEmpty()
+ private val bgScope = applicationScope.plus(coroutineContext)
+
override fun start() {
if (!isFoldable) return
- applicationScope.launch {
+ bgScope.launch {
val foldUpdateLogger = TraceStateLogger("FoldUpdate")
foldStateRepository.foldUpdate.collect { foldUpdateLogger.log(it.name) }
}
- applicationScope.launch {
+ bgScope.launch {
foldStateRepository.hingeAngle.collect {
Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt())
}
}
- applicationScope.launch {
+ bgScope.launch {
val foldedStateLogger = TraceStateLogger("FoldedState")
deviceStateRepository.isFolded.collect { isFolded ->
foldedStateLogger.log(
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 7b628f8d676f..968981197b83 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -20,10 +20,13 @@ import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.os.SystemProperties
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
+import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
@@ -63,6 +66,10 @@ class UnfoldTransitionModule {
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
+ @Provides
+ @UnfoldBgProgressFlag
+ fun unfoldBgProgressFlag() = Flags.unfoldAnimationBackgroundProgress()
+
/** A globally available FoldStateListener that allows one to query the fold state. */
@Provides
@Singleton
@@ -102,7 +109,7 @@ class UnfoldTransitionModule {
@Singleton
fun provideNaturalRotationProgressProvider(
context: Context,
- rotationChangeProvider: RotationChangeProvider,
+ @UnfoldMain rotationChangeProvider: RotationChangeProvider,
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
): Optional<NaturalRotationUnfoldProgressProvider> =
unfoldTransitionProgressProvider.map { provider ->
@@ -153,7 +160,8 @@ class UnfoldTransitionModule {
return resultingProvider?.get()?.orElse(null)?.let { unfoldProgressProvider ->
UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
- } ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ }
+ ?: ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index cc9335edfc14..472f0ae364c5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -14,6 +14,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.plus
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -29,6 +30,14 @@ class CoroutinesModule {
@Provides
@SysUISingleton
+ @Background
+ fun bgApplicationScope(
+ @Application applicationScope: CoroutineScope,
+ @Background coroutineContext: CoroutineContext,
+ ): CoroutineScope = applicationScope.plus(coroutineContext)
+
+ @Provides
+ @SysUISingleton
@Main
@Deprecated(
"Use @Main CoroutineContext instead",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index d8799e16ebdb..43952824f9a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -133,8 +133,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
}
@Test
- public void setScale() throws RemoteException {
- mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mIWindowMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
waitForIdleSync();
verify(mWindowMagnificationController).setScale(3.0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index f4122d59cea1..ea20d29556dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -338,6 +338,13 @@ open class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+
+ // Check credential view persists after new attachment
+ container.onAttachedToWindow()
+
+ assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 67c4e2688cd0..0c30d10ea563 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -51,6 +51,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -58,8 +59,10 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth
-import kotlin.math.max
import kotlin.math.min
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -73,6 +76,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@@ -85,6 +89,47 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
+ @Mock
+ private lateinit var aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var dozingToLockscreenTransitionViewModel:
+ DozingToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var dreamingHostedToLockscreenTransitionViewModel:
+ DreamingHostedToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var dreamingToLockscreenTransitionViewModel:
+ DreamingToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var occludedToLockscreenTransitionViewModel:
+ OccludedToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var primaryBouncerToLockscreenTransitionViewModel:
+ PrimaryBouncerToLockscreenTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToDozingTransitionViewModel:
+ LockscreenToDozingTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToDreamingHostedTransitionViewModel:
+ LockscreenToDreamingHostedTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToDreamingTransitionViewModel:
+ LockscreenToDreamingTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToOccludedTransitionViewModel:
+ LockscreenToOccludedTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToPrimaryBouncerTransitionViewModel:
+ LockscreenToPrimaryBouncerTransitionViewModel
private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
@@ -97,6 +142,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var keyguardInteractor: KeyguardInteractor
+ private val intendedAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(1f)
+ // the viewModel does a `map { 1 - it }` on this value, which is why it's different
+ private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -191,6 +240,31 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
userHandle = UserHandle.SYSTEM,
)
+ intendedAlphaMutableStateFlow.value = 1f
+ intendedShadeAlphaMutableStateFlow.value = 0f
+ whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
+ .thenReturn(intendedAlphaMutableStateFlow)
+ whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(dreamingHostedToLockscreenTransitionViewModel.shortcutsAlpha)
+ .thenReturn(emptyFlow())
+ whenever(dreamingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(goneToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(occludedToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(offToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(primaryBouncerToLockscreenTransitionViewModel.shortcutsAlpha)
+ .thenReturn(emptyFlow())
+ whenever(lockscreenToAodTransitionViewModel.shortcutsAlpha)
+ .thenReturn(intendedAlphaMutableStateFlow)
+ whenever(lockscreenToDozingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(lockscreenToDreamingHostedTransitionViewModel.shortcutsAlpha)
+ .thenReturn(emptyFlow())
+ whenever(lockscreenToDreamingTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(lockscreenToGoneTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
+ whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
+ .thenReturn(emptyFlow())
+ whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+
underTest =
KeyguardQuickAffordancesCombinedViewModel(
quickAffordanceInteractor =
@@ -210,7 +284,27 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
backgroundDispatcher = testDispatcher,
appContext = mContext,
),
- keyguardInteractor = keyguardInteractor
+ keyguardInteractor = keyguardInteractor,
+ shadeInteractor = shadeInteractor,
+ aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+ dreamingHostedToLockscreenTransitionViewModel =
+ dreamingHostedToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+ goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
+ occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+ offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel,
+ primaryBouncerToLockscreenTransitionViewModel =
+ primaryBouncerToLockscreenTransitionViewModel,
+ lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+ lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
+ lockscreenToDreamingHostedTransitionViewModel =
+ lockscreenToDreamingHostedTransitionViewModel,
+ lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
+ lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
+ lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel,
+ lockscreenToPrimaryBouncerTransitionViewModel =
+ lockscreenToPrimaryBouncerTransitionViewModel
)
}
@@ -526,15 +620,15 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@Test
fun isClickable_falseWhenAlphaBelowThreshold() =
testScope.runTest {
+ intendedAlphaMutableStateFlow.value =
+ KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD -
+ .1f
+ // the viewModel does a `map { 1 - it }` on this value, which is why it's different
+ intendedShadeAlphaMutableStateFlow.value =
+ KeyguardQuickAffordancesCombinedViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD +
+ .1f
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
- repository.setKeyguardAlpha(
- max(
- 0f,
- KeyguardQuickAffordancesCombinedViewModel
- .AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - 0.1f
- ),
- )
val testConfig =
TestConfig(
@@ -561,9 +655,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@Test
fun isClickable_falseWhenAlphaAtZero() =
testScope.runTest {
+ intendedAlphaMutableStateFlow.value = 0f
+ intendedShadeAlphaMutableStateFlow.value = 1f
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
- repository.setKeyguardAlpha(0f)
val testConfig =
TestConfig(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e6d6cf263d69..a57feda64723 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -163,23 +163,6 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
}
@Test
- fun alpha_inPreviewMode_doesNotChange() =
- testScope.runTest {
- val value = collectLastValue(underTest.alpha)
- underTest.enablePreviewMode()
-
- assertThat(value()).isEqualTo(1f)
- repository.setKeyguardAlpha(0.1f)
- assertThat(value()).isEqualTo(1f)
- repository.setKeyguardAlpha(0.5f)
- assertThat(value()).isEqualTo(1f)
- repository.setKeyguardAlpha(0.2f)
- assertThat(value()).isEqualTo(1f)
- repository.setKeyguardAlpha(0f)
- assertThat(value()).isEqualTo(1f)
- }
-
- @Test
fun translationAndScaleFromBurnInNotDozing() =
testScope.runTest {
val translationX by collectLastValue(underTest.translationX)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
index a9f8ea0194c1..81d02b8043b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -85,7 +85,7 @@ class CustomTileStatePersisterTest : SysuiTestCase() {
`when`(sharedPreferences.edit()).thenReturn(editor)
tile = Tile()
- customTileStatePersister = CustomTileStatePersister(mockContext)
+ customTileStatePersister = CustomTileStatePersisterImpl(mockContext)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 3808c7ee926b..313ccb8a8717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -228,16 +228,16 @@ class BluetoothTileDialogTest : SysuiTestCase() {
showPairNewDevice = true
)
- val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
- val pairNewLayout =
- bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val seeAllButton = bluetoothTileDialog.requireViewById<View>(R.id.see_all_button)
+ val pairNewButton =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_button)
val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(seeAllLayout).isNotNull()
- assertThat(seeAllLayout.visibility).isEqualTo(GONE)
- assertThat(pairNewLayout).isNotNull()
- assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(seeAllButton).isNotNull()
+ assertThat(seeAllButton.visibility).isEqualTo(GONE)
+ assertThat(pairNewButton).isNotNull()
+ assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
assertThat(adapter.itemCount).isEqualTo(1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index fb5dd212ff22..99993f2b3eff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -30,7 +30,6 @@ import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -113,9 +112,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), any())
- assertThat(bluetoothTileDialogViewModel.dialog?.isShowing).isTrue()
verify(uiEventLogger).log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
}
}
@@ -125,7 +122,6 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -136,7 +132,6 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
backgroundExecutor.execute {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -147,7 +142,6 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(deviceItemInteractor).deviceItemUpdate
}
}
@@ -157,7 +151,6 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(bluetoothStateInteractor).bluetoothStateUpdate
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 4c173cc2956d..e236f4a7730f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -220,45 +220,57 @@ class DeviceItemInteractorTest : SysuiTestCase() {
@Test
fun testUpdateDeviceItemOnClick_connectedMedia_setActive() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).setActive()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).setActive()
+ verify(logger)
+ .logDeviceClick(
+ cachedDevice1.address,
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+ )
+ }
}
@Test
fun testUpdateDeviceItemOnClick_activeMedia_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_connectedOtherDevice_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_saved_connect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).connect()
- verify(logger).logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).connect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ }
}
private fun createFactory(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 5969bd82c361..0173c32bbb94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.flags.ReleasedFlag
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -74,10 +75,15 @@ internal class SceneContainerFlagsTest(
.forEach { flagToken ->
setFlagsRule.enableFlags(flagToken)
aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
+ overrideResource(
+ R.bool.config_sceneContainerFrameworkEnabled,
+ testCase.isResourceConfigEnabled
+ )
}
underTest =
SceneContainerFlagsImpl(
+ context = context,
featureFlagsClassic = featureFlags,
isComposeAvailable = testCase.isComposeAvailable,
)
@@ -91,13 +97,12 @@ internal class SceneContainerFlagsTest(
internal data class TestCase(
val isComposeAvailable: Boolean,
val areAllFlagsSet: Boolean,
+ val isResourceConfigEnabled: Boolean,
val expectedEnabled: Boolean,
) {
override fun toString(): String {
- return """
- (compose=$isComposeAvailable + flags=$areAllFlagsSet) -> expected=$expectedEnabled
- """
- .trimIndent()
+ return "(compose=$isComposeAvailable + flags=$areAllFlagsSet) + XML" +
+ " config=$isResourceConfigEnabled -> expected=$expectedEnabled"
}
}
@@ -105,17 +110,20 @@ internal class SceneContainerFlagsTest(
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun testCases() = buildList {
- repeat(4) { combination ->
- val isComposeAvailable = combination and 0b10 != 0
- val areAllFlagsSet = combination and 0b01 != 0
+ repeat(8) { combination ->
+ val isComposeAvailable = combination and 0b100 != 0
+ val areAllFlagsSet = combination and 0b010 != 0
+ val isResourceConfigEnabled = combination and 0b001 != 0
- val expectedEnabled = isComposeAvailable && areAllFlagsSet
+ val expectedEnabled =
+ isComposeAvailable && areAllFlagsSet && isResourceConfigEnabled
add(
TestCase(
isComposeAvailable = isComposeAvailable,
areAllFlagsSet = areAllFlagsSet,
expectedEnabled = expectedEnabled,
+ isResourceConfigEnabled = isResourceConfigEnabled,
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 6e487cdd65b5..88c728fd1b66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -28,12 +28,16 @@ import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -55,6 +59,8 @@ class BrightnessDialogTest : SysuiTestCase() {
@Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
@Mock private lateinit var brightnessController: BrightnessController
@Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
+ @Mock private lateinit var shadeInteractorLazy: Lazy<ShadeInteractor>
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
private val clock = FakeSystemClock()
private val mainExecutor = FakeExecutor(clock)
@@ -68,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() {
brightnessSliderControllerFactory,
brightnessControllerFactory,
mainExecutor,
- accessibilityMgr
+ accessibilityMgr,
+ shadeInteractor
)
},
/* initialTouchMode= */ false,
@@ -82,6 +89,8 @@ class BrightnessDialogTest : SysuiTestCase() {
.thenReturn(brightnessSliderController)
`when`(brightnessSliderController.rootView).thenReturn(View(context))
`when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
+ whenever(shadeInteractorLazy.get()).thenReturn(shadeInteractor)
+ whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
}
@After
@@ -175,13 +184,15 @@ class BrightnessDialogTest : SysuiTestCase() {
brightnessSliderControllerFactory: BrightnessSliderController.Factory,
brightnessControllerFactory: BrightnessController.Factory,
mainExecutor: DelayableExecutor,
- accessibilityMgr: AccessibilityManagerWrapper
+ accessibilityMgr: AccessibilityManagerWrapper,
+ shadeInteractor: ShadeInteractor
) :
BrightnessDialog(
brightnessSliderControllerFactory,
brightnessControllerFactory,
mainExecutor,
- accessibilityMgr
+ accessibilityMgr,
+ shadeInteractor
) {
private var finishing = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 62c0ebeb8c07..1dbb2972c6f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -22,8 +22,6 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
@@ -295,10 +293,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
)
);
- mActiveNotificationsInteractor = new ActiveNotificationsInteractor(
- new ActiveNotificationListRepository(),
- StandardTestDispatcher(/* scheduler = */ null, /* name = */ null)
- );
+ mActiveNotificationsInteractor =
+ new ActiveNotificationsInteractor(new ActiveNotificationListRepository());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 6374d5e259fc..b86f8410fb7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -25,19 +25,14 @@ import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
@SmallTest
class RenderNotificationsListInteractorTest : SysuiTestCase() {
- private val backgroundDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(backgroundDispatcher)
private val notifsRepository = ActiveNotificationListRepository()
- private val notifsInteractor =
- ActiveNotificationsInteractor(notifsRepository, backgroundDispatcher)
+ private val notifsInteractor = ActiveNotificationsInteractor(notifsRepository)
private val underTest =
RenderNotificationListInteractor(
notifsRepository,
@@ -45,26 +40,21 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
)
@Test
- fun setRenderedList_preservesOrdering() =
- testScope.runTest {
- val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
- val keys = (1..50).shuffled().map { "$it" }
- val entries =
- keys.map {
- mock<ListEntry> {
- val mockRep =
- mock<NotificationEntry> {
- whenever(key).thenReturn(it)
- whenever(sbn).thenReturn(mock())
- whenever(icons).thenReturn(mock())
- }
- whenever(representativeEntry).thenReturn(mockRep)
+ fun setRenderedList_preservesOrdering() = runTest {
+ val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+ val keys = (1..50).shuffled().map { "$it" }
+ val entries =
+ keys.map {
+ mock<ListEntry> {
+ val mockRep = mock<NotificationEntry> {
+ whenever(key).thenReturn(it)
+ whenever(sbn).thenReturn(mock())
+ whenever(icons).thenReturn(mock())
}
+ whenever(representativeEntry).thenReturn(mockRep)
}
- underTest.setRenderedList(entries)
- assertThat(notifs)
- .comparingElementsUsing(byKey)
- .containsExactlyElementsIn(keys)
- .inOrder()
- }
+ }
+ underTest.setRenderedList(entries)
+ assertThat(notifs).comparingElementsUsing(byKey).containsExactlyElementsIn(keys).inOrder()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 755897442eb6..ff5c02622e4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
@@ -170,8 +169,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
new ActiveNotificationListRepository();
private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
- new ActiveNotificationsInteractor(mActiveNotificationsRepository,
- StandardTestDispatcher(/* scheduler = */ null, /* name = */ null));
+ new ActiveNotificationsInteractor(mActiveNotificationsRepository);
private final SeenNotificationsInteractor mSeenNotificationsInteractor =
new SeenNotificationsInteractor(mActiveNotificationsRepository);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
new file mode 100644
index 000000000000..ce471705ed85
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.hardware.devicestate.DeviceStateManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DevicePostureControllerImplTest : SysuiTestCase() {
+ private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
+ private val deviceStateToPostureMapping =
+ arrayOf(
+ "$DEVICE_POSTURE_UNKNOWN:$DEVICE_POSTURE_UNKNOWN",
+ "$DEVICE_POSTURE_CLOSED:$DEVICE_POSTURE_CLOSED",
+ "$DEVICE_POSTURE_HALF_OPENED:$DEVICE_POSTURE_HALF_OPENED",
+ "$DEVICE_POSTURE_OPENED:$DEVICE_POSTURE_OPENED",
+ "$DEVICE_POSTURE_FLIPPED:$DEVICE_POSTURE_FLIPPED",
+ "$useBaseStateDeviceState:1000"
+ )
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
+ @Captor
+ private lateinit var deviceStateCallback: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var testableResources: TestableResources
+ private lateinit var underTest: DevicePostureControllerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mainExecutor = FakeExecutor(FakeSystemClock())
+ testableResources = context.getOrCreateTestableResources()
+ testableResources.addOverride(
+ com.android.internal.R.array.config_device_state_postures,
+ deviceStateToPostureMapping
+ )
+ underTest =
+ DevicePostureControllerImpl(
+ context,
+ deviceStateManager,
+ mainExecutor,
+ )
+ verifyRegistersForDeviceStateCallback()
+ }
+
+ @Test
+ fun testPostureChanged_updates() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+ }
+
+ @Test
+ fun testPostureChanged_useBaseUpdate() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // base state change doesn't change the posture
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // WHEN the display state maps to using the base state, then posture updates
+ deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+ }
+
+ @Test
+ fun baseStateChanges_doesNotUpdatePosture() {
+ var numPostureChanges = 0
+ underTest.addCallback { numPostureChanges++ }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(numPostureChanges).isEqualTo(1)
+
+ // base state changes doesn't send another posture update since the device state isn't
+ // useBaseStateDeviceState
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(numPostureChanges).isEqualTo(1)
+ }
+
+ private fun verifyRegistersForDeviceStateCallback() {
+ verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
new file mode 100644
index 000000000000..4e61b89b9c3e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.progress
+
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.utils.os.FakeHandler
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
+
+ private val wrappedProgressProvider = TestUnfoldTransitionProvider()
+ private val fakeHandler = FakeHandler(Looper.getMainLooper())
+ private val listener = TestUnfoldProgressListener()
+
+ private val progressProvider =
+ MainThreadUnfoldTransitionProgressProvider(fakeHandler, wrappedProgressProvider)
+
+ @Test
+ fun onTransitionStarted_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertStarted()
+ }
+
+ @Test
+ fun onTransitionProgress_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertLastProgress(0.5f)
+ }
+
+ @Test
+ fun onTransitionFinished_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ wrappedProgressProvider.onTransitionFinished()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.ensureTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionFinishing_propagated() {
+ progressProvider.addCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ wrappedProgressProvider.onTransitionProgress(0.5f)
+ wrappedProgressProvider.onTransitionFinished()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.ensureTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionStarted_afterCallbackRemoved_notPropagated() {
+ progressProvider.addCallback(listener)
+ progressProvider.removeCallback(listener)
+
+ wrappedProgressProvider.onTransitionStarted()
+ fakeHandler.dispatchQueuedMessages()
+
+ listener.assertNotStarted()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index 9fe2f5694dde..14fb054a1d9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -15,9 +15,10 @@
*/
package com.android.systemui.unfold.progress
+import android.os.Handler
+import android.os.HandlerThread
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
@@ -26,6 +27,8 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.util.TestFoldStateProvider
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,16 +40,28 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
private val foldStateProvider: TestFoldStateProvider = TestFoldStateProvider()
private val listener = TestUnfoldProgressListener()
private lateinit var progressProvider: UnfoldTransitionProgressProvider
+ private val schedulerFactory =
+ mock<UnfoldFrameCallbackScheduler.Factory>().apply {
+ whenever(create()).then { UnfoldFrameCallbackScheduler() }
+ }
+ private val mockBgHandler = mock<Handler>()
+ private val fakeHandler = Handler(HandlerThread("UnfoldBg").apply { start() }.looper)
@Before
fun setUp() {
- progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(context, foldStateProvider)
+ progressProvider =
+ PhysicsBasedUnfoldTransitionProgressProvider(
+ context,
+ schedulerFactory,
+ foldStateProvider = foldStateProvider,
+ progressHandler = fakeHandler
+ )
progressProvider.addCallback(listener)
}
@Test
fun testUnfold_emitsIncreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
@@ -63,7 +78,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Test
fun testUnfold_emitsFinishingEvent() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
@@ -77,7 +92,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Test
fun testUnfold_screenAvailableOnlyAfterFullUnfold_emitsIncreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendHingeAngleUpdate(90f) },
@@ -94,7 +109,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Test
fun testFold_emitsDecreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_CLOSING) },
{ foldStateProvider.sendHingeAngleUpdate(170f) },
{ foldStateProvider.sendHingeAngleUpdate(90f) },
@@ -110,7 +125,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Test
fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
@@ -126,7 +141,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@Test
fun testFoldImmediatelyAfterUnfold_runsFoldAnimation() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
@@ -144,9 +159,12 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
with(listener.ensureTransitionFinished()) { assertHasFoldAnimationAtTheEnd() }
}
- private fun runOnMainThreadWithInterval(vararg blocks: () -> Unit, intervalMillis: Long = 60) {
+ private fun runOnProgressThreadWithInterval(
+ vararg blocks: () -> Unit,
+ intervalMillis: Long = 60,
+ ) {
blocks.forEach {
- InstrumentationRegistry.getInstrumentation().runOnMainSync { it() }
+ fakeHandler.post(it)
Thread.sleep(intervalMillis)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index aa492871a079..552b60cbeb21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -37,7 +37,6 @@ import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenLis
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.fail
import java.util.concurrent.Executor
@@ -105,16 +104,15 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
foldStateProvider =
DeviceFoldStateProvider(
- config,
- testHingeAngleProvider,
- screenOnStatusProvider,
- foldProvider,
- activityTypeProvider,
- unfoldKeyguardVisibilityProvider,
- rotationChangeProvider,
- context,
- context.mainExecutor,
- handler
+ config,
+ context,
+ screenOnStatusProvider,
+ activityTypeProvider,
+ unfoldKeyguardVisibilityProvider,
+ foldProvider,
+ testHingeAngleProvider,
+ rotationChangeProvider,
+ handler
)
foldStateProvider.addCallback(
@@ -151,6 +149,12 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
null
}
+ whenever(handler.post(any<Runnable>())).then { invocationOnMock ->
+ val runnable = invocationOnMock.getArgument<Runnable>(0)
+ runnable.run()
+ null
+ }
+
// By default, we're on launcher.
setupForegroundActivityType(isHomeActivity = true)
setIsLargeScreen(true)
@@ -171,7 +175,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
@Test
- fun testOnUnfold_hingeAngleDecreasesBeforeInnerScreenAvailable_emitsOnlyStartAndInnerScreenAvailableEvents() {
+ fun onUnfold_angleDecrBeforeInnerScrAvailable_emitsOnlyStartAndInnerScrAvailableEvents() {
setFoldState(folded = true)
foldUpdates.clear()
@@ -187,7 +191,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
@Test
- fun testOnUnfold_hingeAngleDecreasesAfterInnerScreenAvailable_emitsStartInnerScreenAvailableAndStartClosingEvents() {
+ fun onUnfold_angleDecrAfterInnerScrAvailable_emitsStartInnerScrAvailableAndStartClosingEvnts() {
setFoldState(folded = true)
foldUpdates.clear()
@@ -690,7 +694,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
callbacks.forEach { it.onFoldUpdated(isFolded) }
}
- fun getNumberOfCallbacks(): Int{
+ fun getNumberOfCallbacks(): Int {
return callbacks.size
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
new file mode 100644
index 000000000000..29702eb2cdc9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.service.quicksettings.Tile
+
+class FakeCustomTileStatePersister : CustomTileStatePersister {
+
+ private val tiles: MutableMap<TileServiceKey, Tile> = mutableMapOf()
+
+ override fun readState(key: TileServiceKey): Tile? = tiles[key]
+
+ override fun persistState(key: TileServiceKey, tile: Tile) {
+ tiles[key] = tile
+ }
+
+ override fun removeState(key: TileServiceKey) {
+ tiles.remove(key)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
new file mode 100644
index 000000000000..d2351dc8ae18
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom
+
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.tiles
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
+
+/**
+ * [Tile]-specific extension for [Truth]. Use [assertThat] or [tiles] to get an instance of this
+ * subject.
+ */
+class TileSubject private constructor(failureMetadata: FailureMetadata, subject: Tile?) :
+ Subject(failureMetadata, subject) {
+
+ private val actual: Tile? = subject
+
+ /** Asserts if the [Tile] fields are the same. */
+ fun isEqualTo(other: Tile?) {
+ if (actual == null) {
+ check("other").that(other).isNull()
+ return
+ } else {
+ check("other").that(other).isNotNull()
+ other ?: return
+ }
+
+ check("icon").that(actual.icon).isEqualTo(other.icon)
+ check("label").that(actual.label).isEqualTo(other.label)
+ check("subtitle").that(actual.subtitle).isEqualTo(other.subtitle)
+ check("contentDescription")
+ .that(actual.contentDescription)
+ .isEqualTo(other.contentDescription)
+ check("stateDescription").that(actual.stateDescription).isEqualTo(other.stateDescription)
+ check("activityLaunchForClick")
+ .that(actual.activityLaunchForClick)
+ .isEqualTo(other.activityLaunchForClick)
+ check("state").that(actual.state).isEqualTo(other.state)
+ }
+
+ companion object {
+
+ /** Returns a factory to be used with [Truth.assertAbout]. */
+ fun tiles(): Factory<TileSubject, Tile?> {
+ return Factory { failureMetadata: FailureMetadata, subject: Tile? ->
+ TileSubject(failureMetadata, subject)
+ }
+ }
+
+ /** Shortcut for `Truth.assertAbout(tiles()).that(tile)`. */
+ fun assertThat(tile: Tile?): TileSubject = Truth.assertAbout(tiles()).that(tile)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
index 13910fd5c564..ccba07273f1e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -19,15 +19,20 @@ package com.android.systemui.qs.tiles.impl.custom.data.repository
import android.content.ComponentName
import android.os.UserHandle
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
private val defaults: MutableMap<DefaultsKey, CustomTileDefaults> = mutableMapOf()
- private val defaultsFlow = MutableSharedFlow<DefaultsRequest>()
+ private val defaultsFlow =
+ MutableSharedFlow<DefaultsRequest>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
val defaultsRequests: List<DefaultsRequest> = mutableDefaultsRequests
@@ -41,7 +46,7 @@ class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
old == new
}
}
- .map { defaults[DefaultsKey(it.user, it.componentName)]!! }
+ .mapNotNull { defaults[DefaultsKey(it.user, it.componentName)] }
override fun requestNewDefaults(
user: UserHandle,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
new file mode 100644
index 000000000000..ccf03911495f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+
+class FakeCustomTileRepository(
+ tileSpec: TileSpec.CustomTileSpec,
+ customTileStatePersister: FakeCustomTileStatePersister,
+ testBackgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val realDelegate: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ tileSpec,
+ customTileStatePersister,
+ testBackgroundContext,
+ )
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) =
+ realDelegate.restoreForTheUserIfNeeded(user, isPersistable)
+
+ override fun getTiles(user: UserHandle): Flow<Tile> = realDelegate.getTiles(user)
+
+ override fun getTile(user: UserHandle): Tile? = realDelegate.getTile(user)
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithTile(user, newTile, isPersistable)
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithDefaults(user, defaults, isPersistable)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
index 01f453570e63..3d7fb6d91393 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt
@@ -17,10 +17,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
val Kosmos.activeNotificationsInteractor by
- Kosmos.Fixture {
- ActiveNotificationsInteractor(activeNotificationListRepository, testDispatcher)
- }
+ Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
index a639df539cb9..2bc2db3ba629 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
@@ -16,9 +16,12 @@
package com.android.systemui.unfold
+import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.dagger.UseReceivingFilter
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
+import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
import dagger.Module
import dagger.Provides
@@ -33,16 +36,25 @@ class UnfoldRemoteModule {
@Singleton
fun provideTransitionProvider(
config: UnfoldTransitionConfig,
- traceListener: ATraceLoggerTransitionProgressListener,
+ traceListener: ATraceLoggerTransitionProgressListener.Factory,
remoteReceiverProvider: Provider<RemoteUnfoldTransitionReceiver>,
): Optional<RemoteUnfoldTransitionReceiver> {
if (!config.isEnabled) {
return Optional.empty()
}
val remoteReceiver = remoteReceiverProvider.get()
- remoteReceiver.addCallback(traceListener)
+ remoteReceiver.addCallback(traceListener.create("remoteReceiver"))
return Optional.of(remoteReceiver)
}
@Provides @UseReceivingFilter fun useReceivingFilter(): Boolean = true
+
+ @Provides
+ @UnfoldMain
+ fun provideMainRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldMain mainHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(mainHandler)
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index c3a6cf035d09..31b7ccca49ac 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -22,12 +22,12 @@ import android.hardware.SensorManager
import android.hardware.display.DisplayManager
import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
-import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
@@ -63,13 +63,12 @@ interface UnfoldSharedComponent {
@BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
@BindsInstance displayManager: DisplayManager,
- @BindsInstance contentResolver: ContentResolver = context.contentResolver
+ @BindsInstance @UnfoldBg bgHandler: Handler,
+ @BindsInstance contentResolver: ContentResolver = context.contentResolver,
): UnfoldSharedComponent
}
val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
- val hingeAngleProvider: HingeAngleProvider
- val rotationChangeProvider: RotationChangeProvider
}
/**
@@ -94,7 +93,8 @@ interface RemoteUnfoldSharedComponent {
}
val remoteTransitionProgress: Optional<RemoteUnfoldTransitionReceiver>
- val rotationChangeProvider: RotationChangeProvider
+
+ @UnfoldMain fun getRotationChangeProvider(): RotationChangeProvider
}
/**
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 7473ca6a6486..f7fb01465a40 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -16,14 +16,20 @@
package com.android.systemui.unfold
+import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBg
+import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
+import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.MainThreadUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateRepository
import com.android.systemui.unfold.updates.FoldStateRepositoryImpl
+import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -32,22 +38,27 @@ import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManagerImpl
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
+import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
import javax.inject.Singleton
+import kotlin.jvm.optionals.getOrDefault
-@Module(includes = [UnfoldSharedInternalModule::class])
+@Module(
+ includes =
+ [
+ UnfoldFlagsModule::class,
+ UnfoldSharedInternalModule::class,
+ UnfoldRotationProviderInternalModule::class,
+ HingeAngleProviderInternalModule::class,
+ FoldStateProviderModule::class,
+ ]
+)
class UnfoldSharedModule {
@Provides
@Singleton
- fun provideFoldStateProvider(
- deviceFoldStateProvider: DeviceFoldStateProvider
- ): FoldStateProvider = deviceFoldStateProvider
-
- @Provides
- @Singleton
fun unfoldKeyguardVisibilityProvider(
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityProvider = impl
@@ -60,9 +71,17 @@ class UnfoldSharedModule {
@Provides
@Singleton
- fun foldStateRepository(
- impl: FoldStateRepositoryImpl
- ): FoldStateRepository = impl
+ fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
+}
+
+@Module
+abstract class UnfoldFlagsModule {
+ /**
+ * Users of the library can bind this boolean to notify whether the progress should be
+ * calculated only in the background (and the main thread provider is generated by posting the
+ * background events in the main handler).
+ */
+ @BindsOptionalOf @UnfoldBgProgressFlag abstract fun unfoldBgProgressFlag(): Boolean
}
/**
@@ -77,17 +96,86 @@ internal class UnfoldSharedInternalModule {
fun unfoldTransitionProgressProvider(
config: UnfoldTransitionConfig,
scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+ tracingListener: ATraceLoggerTransitionProgressListener.Factory,
+ physicsBasedUnfoldTransitionProgressProvider:
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
+ fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ foldStateProvider: FoldStateProvider,
+ @UnfoldMain mainHandler: Handler,
+ mainThreadUnfoldTransitionProgressProviderFactory:
+ MainThreadUnfoldTransitionProgressProvider.Factory,
+ @UnfoldBg bgProvider: Provider<Optional<UnfoldTransitionProgressProvider>>,
+ @UnfoldBgProgressFlag unfoldBgProgressFlag: Optional<Boolean>,
+ ): Optional<UnfoldTransitionProgressProvider> {
+ if (unfoldBgProgressFlag.getOrDefault(false)) {
+ // In this case, we wrap the background progress provider
+ val mainThreadProvider: Optional<UnfoldTransitionProgressProvider> =
+ bgProvider.get().map {
+ mainThreadUnfoldTransitionProgressProviderFactory.create(it)
+ }
+ mainThreadProvider.ifPresent {
+ it.addCallback(tracingListener.create("MainThreadFromBgProgress"))
+ }
+ return mainThreadProvider
+ } else {
+ // TODO(b/277879146): Remove this once unfold_animation_background_progress is launched.
+ return createOptionalUnfoldTransitionProgressProvider(
+ config = config,
+ scaleAwareProviderFactory = scaleAwareProviderFactory,
+ tracingListener = tracingListener.create("MainThread"),
+ physicsBasedUnfoldTransitionProgressProvider =
+ physicsBasedUnfoldTransitionProgressProvider,
+ fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+ foldStateProvider = foldStateProvider,
+ progressHandler = mainHandler,
+ )
+ }
+ }
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun unfoldBgTransitionProgressProvider(
+ config: UnfoldTransitionConfig,
+ scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+ tracingListener: ATraceLoggerTransitionProgressListener.Factory,
+ physicsBasedUnfoldTransitionProgressProvider:
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
+ fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ @UnfoldBg bgFoldStateProvider: FoldStateProvider,
+ @UnfoldBg bgHandler: Handler,
+ ): Optional<UnfoldTransitionProgressProvider> {
+ return createOptionalUnfoldTransitionProgressProvider(
+ config = config,
+ scaleAwareProviderFactory = scaleAwareProviderFactory,
+ tracingListener = tracingListener.create("BgThread"),
+ physicsBasedUnfoldTransitionProgressProvider =
+ physicsBasedUnfoldTransitionProgressProvider,
+ fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+ foldStateProvider = bgFoldStateProvider,
+ progressHandler = bgHandler,
+ )
+ }
+
+ private fun createOptionalUnfoldTransitionProgressProvider(
+ config: UnfoldTransitionConfig,
+ scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
tracingListener: ATraceLoggerTransitionProgressListener,
physicsBasedUnfoldTransitionProgressProvider:
- Provider<PhysicsBasedUnfoldTransitionProgressProvider>,
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ foldStateProvider: FoldStateProvider,
+ progressHandler: Handler,
): Optional<UnfoldTransitionProgressProvider> {
if (!config.isEnabled) {
return Optional.empty()
}
val baseProgressProvider =
if (config.isHingeAngleEnabled) {
- physicsBasedUnfoldTransitionProgressProvider.get()
+ physicsBasedUnfoldTransitionProgressProvider.create(
+ foldStateProvider,
+ progressHandler
+ )
} else {
fixedTimingTransitionProgressProvider.get()
}
@@ -101,26 +189,105 @@ internal class UnfoldSharedInternalModule {
}
@Provides
+ @Singleton
+ fun provideProgressForwarder(
+ config: UnfoldTransitionConfig,
+ progressForwarder: Provider<UnfoldTransitionProgressForwarder>
+ ): Optional<UnfoldTransitionProgressForwarder> {
+ if (!config.isEnabled) {
+ return Optional.empty()
+ }
+ return Optional.of(progressForwarder.get())
+ }
+}
+
+/**
+ * Provides [FoldStateProvider]. The [UnfoldBg] annotated binding sends progress in the [UnfoldBg]
+ * handler.
+ */
+@Module
+internal class FoldStateProviderModule {
+ @Provides
+ @Singleton
+ fun provideFoldStateProvider(
+ factory: DeviceFoldStateProvider.Factory,
+ @UnfoldMain hingeAngleProvider: HingeAngleProvider,
+ @UnfoldMain rotationChangeProvider: RotationChangeProvider,
+ @UnfoldMain mainHandler: Handler,
+ ): FoldStateProvider =
+ factory.create(
+ hingeAngleProvider,
+ rotationChangeProvider,
+ progressHandler = mainHandler
+ )
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun provideBgFoldStateProvider(
+ factory: DeviceFoldStateProvider.Factory,
+ @UnfoldBg hingeAngleProvider: HingeAngleProvider,
+ @UnfoldBg rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg bgHandler: Handler,
+ ): FoldStateProvider =
+ factory.create(
+ hingeAngleProvider,
+ rotationChangeProvider,
+ progressHandler = bgHandler
+ )
+}
+
+/** Provides bindings for both [UnfoldMain] and [UnfoldBg] [HingeAngleProvider]. */
+@Module
+internal class HingeAngleProviderInternalModule {
+ @Provides
+ @UnfoldMain
fun hingeAngleProvider(
config: UnfoldTransitionConfig,
- hingeAngleSensorProvider: Provider<HingeSensorAngleProvider>
+ @UnfoldMain handler: Handler,
+ hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
): HingeAngleProvider {
return if (config.isHingeAngleEnabled) {
- hingeAngleSensorProvider.get()
+ hingeAngleSensorProvider.create(handler)
} else {
EmptyHingeAngleProvider
}
}
@Provides
- @Singleton
- fun provideProgressForwarder(
- config: UnfoldTransitionConfig,
- progressForwarder: Provider<UnfoldTransitionProgressForwarder>
- ): Optional<UnfoldTransitionProgressForwarder> {
- if (!config.isEnabled) {
- return Optional.empty()
+ @UnfoldBg
+ fun hingeAngleProviderBg(
+ config: UnfoldTransitionConfig,
+ @UnfoldBg handler: Handler,
+ hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
+ ): HingeAngleProvider {
+ return if (config.isHingeAngleEnabled) {
+ hingeAngleSensorProvider.create(handler)
+ } else {
+ EmptyHingeAngleProvider
}
- return Optional.of(progressForwarder.get())
+ }
+}
+
+@Module
+internal class UnfoldRotationProviderInternalModule {
+ @Provides
+ @Singleton
+ @UnfoldMain
+ fun provideRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldMain mainHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(mainHandler)
+ }
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun provideBgRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldBg bgHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(bgHandler)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 18399194434a..1cbaf3135c4d 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -48,6 +48,7 @@ fun createUnfoldSharedComponent(
singleThreadBgExecutor: Executor,
tracingTagPrefix: String,
displayManager: DisplayManager,
+ bgHandler: Handler,
): UnfoldSharedComponent =
DaggerUnfoldSharedComponent.factory()
.create(
@@ -62,6 +63,7 @@ fun createUnfoldSharedComponent(
singleThreadBgExecutor,
tracingTagPrefix,
displayManager,
+ bgHandler,
)
/**
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt
new file mode 100644
index 000000000000..7cd441950517
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.unfold.dagger
+
+import javax.inject.Qualifier
+
+/** Annotation for background computations related to unfold lib. */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBg
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
index 2fe4fd5a12e4..0e371fa6bfb7 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBgProgressFlag.kt
@@ -5,7 +5,7 @@
* 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.0N
+ * 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,
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.credentialmanager.model
+package com.android.systemui.unfold.dagger
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
+import javax.inject.Qualifier
-data class Password(
- val providerId: String,
- val entry: Entry,
- val passwordCredentialEntry: PasswordCredentialEntry,
-)
+/**
+ * Annotates the boolean representing whether we are calculating progresses in the background.
+ *
+ * Used to allow clients to provide this value, without depending on the flags directly.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBgProgressFlag
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
new file mode 100644
index 000000000000..9bdf3d5d5307
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold.progress
+
+import android.os.Handler
+import androidx.annotation.FloatRange
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldMain
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * [UnfoldTransitionProgressProvider] that forwards all progress to the main thread handler.
+ *
+ * This is needed when progress are calculated in the background, but some listeners need the
+ * callbacks in the main thread.
+ */
+class MainThreadUnfoldTransitionProgressProvider
+@AssistedInject
+constructor(
+ @UnfoldMain private val mainHandler: Handler,
+ @Assisted private val rootProvider: UnfoldTransitionProgressProvider
+) : UnfoldTransitionProgressProvider {
+
+ private val listenerMap = mutableMapOf<TransitionProgressListener, TransitionProgressListener>()
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ assertMainThread()
+ val proxy = TransitionProgressListerProxy(listener)
+ rootProvider.addCallback(proxy)
+ listenerMap[listener] = proxy
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ assertMainThread()
+ val proxy = listenerMap.remove(listener) ?: return
+ rootProvider.removeCallback(proxy)
+ }
+
+ private fun assertMainThread() {
+ check(mainHandler.looper.isCurrentThread) {
+ "Should be called from the main thread, but this is ${Thread.currentThread()}"
+ }
+ }
+
+ override fun destroy() {
+ rootProvider.destroy()
+ }
+
+ inner class TransitionProgressListerProxy(private val listener: TransitionProgressListener) :
+ TransitionProgressListener {
+ override fun onTransitionStarted() {
+ mainHandler.post { listener.onTransitionStarted() }
+ }
+
+ override fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
+ mainHandler.post { listener.onTransitionProgress(progress) }
+ }
+
+ override fun onTransitionFinishing() {
+ mainHandler.post { listener.onTransitionFinishing() }
+ }
+
+ override fun onTransitionFinished() {
+ mainHandler.post { listener.onTransitionFinished() }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a [MainThreadUnfoldTransitionProgressProvider] that wraps the [rootProvider]. */
+ fun create(
+ rootProvider: UnfoldTransitionProgressProvider
+ ): MainThreadUnfoldTransitionProgressProvider
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index f8f168bd4dc1..907bf46fb981 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
+import android.os.Handler
import android.os.Trace
import android.util.FloatProperty
import android.util.Log
@@ -38,13 +39,25 @@ import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.systemui.unfold.updates.name
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-/** Maps fold updates to unfold transition progress using DynamicAnimation. */
+/**
+ * Maps fold updates to unfold transition progress using DynamicAnimation.
+ *
+ * Note that all variable accesses must be done in the [Handler] provided in the constructor, that
+ * might be different than [mainHandler]. When a custom handler is provided, the [SpringAnimation]
+ * uses a scheduler different than the default one.
+ */
class PhysicsBasedUnfoldTransitionProgressProvider
-@Inject
-constructor(context: Context, private val foldStateProvider: FoldStateProvider) :
- UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
+@AssistedInject
+constructor(
+ context: Context,
+ private val schedulerFactory: UnfoldFrameCallbackScheduler.Factory,
+ @Assisted private val foldStateProvider: FoldStateProvider,
+ @Assisted private val progressHandler: Handler,
+) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
private val emphasizedInterpolator =
loadInterpolator(context, android.R.interpolator.fast_out_extra_slow_in)
@@ -63,6 +76,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
private var transitionProgress: Float = 0.0f
set(value) {
+ assertInProgressThread()
if (isTransitionRunning) {
listeners.forEach { it.onTransitionProgress(value) }
}
@@ -72,8 +86,14 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
init {
- foldStateProvider.addCallback(this)
- foldStateProvider.start()
+ progressHandler.post {
+ // The scheduler needs to be created in the progress handler in order to get the correct
+ // choreographer and frame callbacks. This is because the choreographer can be get only
+ // as a thread local.
+ springAnimation.scheduler = schedulerFactory.create()
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
}
override fun destroy() {
@@ -81,6 +101,8 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
}
override fun onHingeAngleUpdate(angle: Float) {
+ assertInProgressThread()
+
if (!isTransitionRunning || isAnimatedCancelRunning) return
val progress = saturate(angle / FINAL_HINGE_ANGLE_POSITION)
springAnimation.animateToFinalPosition(progress)
@@ -90,6 +112,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
if (amount < low) low else if (amount > high) high else amount
override fun onFoldUpdate(@FoldUpdate update: Int) {
+ assertInProgressThread()
when (update) {
FOLD_UPDATE_FINISH_FULL_OPEN,
FOLD_UPDATE_FINISH_HALF_OPEN -> {
@@ -148,6 +171,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
}
private fun cancelTransition(endValue: Float, animate: Boolean) {
+ assertInProgressThread()
if (isTransitionRunning && animate) {
if (endValue == 1.0f && !isAnimatedCancelRunning) {
listeners.forEach { it.onTransitionFinishing() }
@@ -165,7 +189,6 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
isAnimatedCancelRunning = false
isTransitionRunning = false
springAnimation.cancel()
-
cannedAnimator?.removeAllListeners()
cannedAnimator?.cancel()
cannedAnimator = null
@@ -182,7 +205,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
animation: DynamicAnimation<out DynamicAnimation<*>>,
canceled: Boolean,
value: Float,
- velocity: Float
+ velocity: Float,
) {
if (isAnimatedCancelRunning) {
cancelTransition(value, animate = false)
@@ -202,6 +225,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
}
private fun startTransition(startValue: Float) {
+ assertInProgressThread()
if (!isTransitionRunning) onStartTransition()
springAnimation.apply {
@@ -221,14 +245,16 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
}
override fun addCallback(listener: TransitionProgressListener) {
- listeners.add(listener)
+ progressHandler.post { listeners.add(listener) }
}
override fun removeCallback(listener: TransitionProgressListener) {
- listeners.remove(listener)
+ progressHandler.post { listeners.remove(listener) }
}
private fun startCannedCancelAnimation() {
+ assertInProgressThread()
+
cannedAnimator?.cancel()
cannedAnimator = null
@@ -264,7 +290,7 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
override fun setValue(
provider: PhysicsBasedUnfoldTransitionProgressProvider,
- value: Float
+ value: Float,
) {
provider.transitionProgress = value
}
@@ -272,6 +298,25 @@ constructor(context: Context, private val foldStateProvider: FoldStateProvider)
override fun get(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
provider.transitionProgress
}
+
+ private fun assertInProgressThread() {
+ check(progressHandler.looper.isCurrentThread) {
+ val progressThread = progressHandler.looper.thread
+ val thisThread = Thread.currentThread()
+ """should be called from the progress thread.
+ progressThread=$progressThread tid=${progressThread.id}
+ Thread.currentThread()=$thisThread tid=${thisThread.id}"""
+ .trimMargin()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ foldStateProvider: FoldStateProvider,
+ handler: Handler,
+ ): PhysicsBasedUnfoldTransitionProgressProvider
+ }
}
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt
new file mode 100644
index 000000000000..1dffd84f8242
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.progress
+
+import android.os.Looper
+import android.view.Choreographer
+import androidx.dynamicanimation.animation.FrameCallbackScheduler
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Scheduler that posts animation progresses on a thread different than the ui one.
+ *
+ * The following is taken from [AnimationHandler.FrameCallbackScheduler16]. It is extracted here as
+ * there are no guarantees which implementation the [DynamicAnimation] class would use otherwise.
+ * This allows classes using [DynamicAnimation] to be created in any thread, but still use the
+ * scheduler for a specific thread.
+ *
+ * Technically the [AssistedInject] is not needed: it's just to have a nicer factory with a
+ * documentation snippet instead of using a plain dagger provider.
+ */
+class UnfoldFrameCallbackScheduler @AssistedInject constructor() : FrameCallbackScheduler {
+
+ private val choreographer = Choreographer.getInstance()
+ private val looper =
+ Looper.myLooper() ?: error("This should be created in a thread with a looper.")
+
+ override fun postFrameCallback(frameCallback: Runnable) {
+ choreographer.postFrameCallback { frameCallback.run() }
+ }
+
+ override fun isCurrentThread(): Boolean {
+ return looper.isCurrentThread
+ }
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a [FrameCallbackScheduler] that uses [Choreographer] to post frame callbacks.
+ *
+ * Note that the choreographer used depends on the thread this [create] is called on, as it
+ * is get from a thread static attribute.
+ */
+ fun create(): UnfoldFrameCallbackScheduler
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 003013e18583..77f637bb8ba1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -23,37 +23,34 @@ import androidx.annotation.VisibleForTesting
import androidx.core.util.Consumer
import com.android.systemui.unfold.compat.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldMain
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
-import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
-import javax.inject.Inject
class DeviceFoldStateProvider
-@Inject
+@AssistedInject
constructor(
config: UnfoldTransitionConfig,
- private val hingeAngleProvider: HingeAngleProvider,
+ private val context: Context,
private val screenStatusProvider: ScreenStatusProvider,
- private val foldProvider: FoldProvider,
private val activityTypeProvider: CurrentActivityTypeProvider,
private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider,
- private val rotationChangeProvider: RotationChangeProvider,
- private val context: Context,
- @UnfoldMain private val mainExecutor: Executor,
- @UnfoldMain private val handler: Handler
+ private val foldProvider: FoldProvider,
+ @Assisted private val hingeAngleProvider: HingeAngleProvider,
+ @Assisted private val rotationChangeProvider: RotationChangeProvider,
+ @Assisted private val progressHandler: Handler,
) : FoldStateProvider {
+ private val outputListeners = CopyOnWriteArrayList<FoldStateProvider.FoldUpdatesListener>()
- private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
-
- @FoldUpdate private var lastFoldUpdate: Int? = null
+ @FoldStateProvider.FoldUpdate private var lastFoldUpdate: Int? = null
@FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
@FloatRange(from = 0.0, to = 180.0) private var lastHingeAngleBeforeTransition: Float = 0f
@@ -61,11 +58,9 @@ constructor(
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener()
- private val mainLooper = handler.looper
private val timeoutRunnable = Runnable { cancelAnimation() }
- private val rotationListener = RotationListener {
- if (isTransitionInProgress) cancelAnimation()
- }
+ private val rotationListener = FoldRotationListener()
+ private val progressExecutor = Executor { progressHandler.post(it) }
/**
* Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
@@ -80,9 +75,9 @@ constructor(
private var isStarted = false
override fun start() {
- assertMainThread()
if (isStarted) return
- foldProvider.registerCallback(foldStateListener, mainExecutor)
+ foldProvider.registerCallback(foldStateListener, progressExecutor)
+ // TODO(b/277879146): get callbacks in the background
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
rotationChangeProvider.addCallback(rotationListener)
@@ -91,7 +86,6 @@ constructor(
}
override fun stop() {
- assertMainThread()
screenStatusProvider.removeCallback(screenListener)
foldProvider.unregisterCallback(foldStateListener)
hingeAngleProvider.removeCallback(hingeAngleListener)
@@ -101,11 +95,11 @@ constructor(
isStarted = false
}
- override fun addCallback(listener: FoldUpdatesListener) {
+ override fun addCallback(listener: FoldStateProvider.FoldUpdatesListener) {
outputListeners.add(listener)
}
- override fun removeCallback(listener: FoldUpdatesListener) {
+ override fun removeCallback(listener: FoldStateProvider.FoldUpdatesListener) {
outputListeners.remove(listener)
}
@@ -121,6 +115,7 @@ constructor(
lastFoldUpdate == FOLD_UPDATE_START_CLOSING
private fun onHingeAngle(angle: Float) {
+ assertInProgressThread()
if (DEBUG) {
Log.d(
TAG,
@@ -131,14 +126,14 @@ constructor(
}
val currentDirection =
- if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
+ if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
if (isTransitionInProgress && currentDirection != lastFoldUpdate) {
lastHingeAngleBeforeTransition = lastHingeAngle
}
val isClosing = angle < lastHingeAngleBeforeTransition
val transitionUpdate =
- if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
+ if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
val angleChangeSurpassedThreshold =
Math.abs(angle - lastHingeAngleBeforeTransition) > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES
val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
@@ -150,12 +145,12 @@ constructor(
angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle
eventNotAlreadyDispatched && // we haven't sent transition event already
!isFullyOpened && // do not send transition event if we are in fully opened hinge
- // angle range as closing threshold could overlap this range
+ // angle range as closing threshold could overlap this range
screenAvailableEventSent && // do not send transition event if we are still in the
- // process of turning on the inner display
+ // process of turning on the inner display
isClosingThresholdMet(angle) && // hinge angle is below certain threshold.
isOnLargeScreen // Avoids sending closing event when on small screen.
- // Start event is sent regardless due to hall sensor.
+ // Start event is sent regardless due to hall sensor.
) {
notifyFoldUpdate(transitionUpdate, lastHingeAngle)
}
@@ -202,6 +197,7 @@ constructor(
private inner class FoldStateListener : FoldProvider.FoldCallback {
override fun onFoldUpdated(isFolded: Boolean) {
+ assertInProgressThread()
this@DeviceFoldStateProvider.isFolded = isFolded
lastHingeAngle = FULLY_CLOSED_DEGREES
@@ -218,7 +214,14 @@ constructor(
}
}
- private fun notifyFoldUpdate(@FoldUpdate update: Int, angle: Float) {
+ private inner class FoldRotationListener : RotationChangeProvider.RotationListener {
+ override fun onRotationChanged(newRotation: Int) {
+ assertInProgressThread()
+ if (isTransitionInProgress) cancelAnimation()
+ }
+ }
+
+ private fun notifyFoldUpdate(@FoldStateProvider.FoldUpdate update: Int, angle: Float) {
if (DEBUG) {
Log.d(TAG, update.name())
}
@@ -236,11 +239,11 @@ constructor(
if (isTransitionInProgress) {
cancelTimeout()
}
- handler.postDelayed(timeoutRunnable, halfOpenedTimeoutMillis.toLong())
+ progressHandler.postDelayed(timeoutRunnable, halfOpenedTimeoutMillis.toLong())
}
private fun cancelTimeout() {
- handler.removeCallbacks(timeoutRunnable)
+ progressHandler.removeCallbacks(timeoutRunnable)
}
private fun cancelAnimation(): Unit =
@@ -249,42 +252,61 @@ constructor(
private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
override fun onScreenTurnedOn() {
- // Trigger this event only if we are unfolded and this is the first screen
- // turned on event since unfold started. This prevents running the animation when
- // turning on the internal display using the power button.
- // Initially isUnfoldHandled is true so it will be reset to false *only* when we
- // receive 'folded' event. If SystemUI started when device is already folded it will
- // still receive 'folded' event on startup.
- if (!isFolded && !isUnfoldHandled) {
- outputListeners.forEach { it.onUnfoldedScreenAvailable() }
- isUnfoldHandled = true
+ executeInProgressThread {
+ // Trigger this event only if we are unfolded and this is the first screen
+ // turned on event since unfold started. This prevents running the animation when
+ // turning on the internal display using the power button.
+ // Initially isUnfoldHandled is true so it will be reset to false *only* when we
+ // receive 'folded' event. If SystemUI started when device is already folded it will
+ // still receive 'folded' event on startup.
+ if (!isFolded && !isUnfoldHandled) {
+ outputListeners.forEach { it.onUnfoldedScreenAvailable() }
+ isUnfoldHandled = true
+ }
}
}
override fun markScreenAsTurnedOn() {
- if (!isFolded) {
- isUnfoldHandled = true
+ executeInProgressThread {
+ if (!isFolded) {
+ isUnfoldHandled = true
+ }
}
}
override fun onScreenTurningOn() {
- isScreenOn = true
- updateHingeAngleProviderState()
+ executeInProgressThread {
+ isScreenOn = true
+ updateHingeAngleProviderState()
+ }
}
override fun onScreenTurningOff() {
- isScreenOn = false
- updateHingeAngleProviderState()
+ executeInProgressThread {
+ isScreenOn = false
+ updateHingeAngleProviderState()
+ }
+ }
+
+ /**
+ * Needed just for compatibility while not all data sources are providing data in the
+ * background.
+ *
+ * TODO(b/277879146): Remove once ScreeStatusProvider provides in the background.
+ */
+ private fun executeInProgressThread(f: () -> Unit) {
+ progressHandler.post { f() }
}
}
private fun isOnLargeScreen(): Boolean {
- return context.resources.configuration.smallestScreenWidthDp >
- INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
+ return context.resources.configuration.smallestScreenWidthDp >
+ INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
}
/** While the screen is off or the device is folded, hinge angle updates are not needed. */
private fun updateHingeAngleProviderState() {
+ assertInProgressThread()
if (isScreenOn && !isFolded) {
hingeAngleProvider.start()
} else {
@@ -294,20 +316,34 @@ constructor(
private inner class HingeAngleListener : Consumer<Float> {
override fun accept(angle: Float) {
+ assertInProgressThread()
onHingeAngle(angle)
}
}
- private fun assertMainThread() {
- check(mainLooper.isCurrentThread) {
- ("should be called from the main thread." +
- " sMainLooper.threadName=" + mainLooper.thread.name +
- " Thread.currentThread()=" + Thread.currentThread().name)
+ private fun assertInProgressThread() {
+ check(progressHandler.looper.isCurrentThread) {
+ val progressThread = progressHandler.looper.thread
+ val thisThread = Thread.currentThread()
+ """should be called from the progress thread.
+ progressThread=$progressThread tid=${progressThread.id}
+ Thread.currentThread()=$thisThread tid=${thisThread.id}"""
+ .trimMargin()
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a [DeviceFoldStateProvider] using the provided dependencies. */
+ fun create(
+ hingeAngleProvider: HingeAngleProvider,
+ rotationChangeProvider: RotationChangeProvider,
+ progressHandler: Handler,
+ ): DeviceFoldStateProvider
+ }
}
-fun @receiver:FoldUpdate Int.name() =
+fun @receiver:FoldStateProvider.FoldUpdate Int.name() =
when (this) {
FOLD_UPDATE_START_OPENING -> "START_OPENING"
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index ce8f1a178d05..82ea362e8049 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -20,20 +20,21 @@ import android.content.Context
import android.hardware.display.DisplayManager
import android.os.Handler
import android.os.RemoteException
-import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.util.CallbackController
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
/**
- * Allows to subscribe to rotation changes. Updates are provided for the display associated
- * to [context].
+ * Allows to subscribe to rotation changes. Updates are provided for the display associated to
+ * [context].
*/
class RotationChangeProvider
-@Inject
+@AssistedInject
constructor(
private val displayManager: DisplayManager,
private val context: Context,
- @UnfoldMain private val mainHandler: Handler,
+ @Assisted private val handler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
private val listeners = mutableListOf<RotationListener>()
@@ -42,7 +43,7 @@ constructor(
private var lastRotation: Int? = null
override fun addCallback(listener: RotationListener) {
- mainHandler.post {
+ handler.post {
if (listeners.isEmpty()) {
subscribeToRotation()
}
@@ -51,7 +52,7 @@ constructor(
}
override fun removeCallback(listener: RotationListener) {
- mainHandler.post {
+ handler.post {
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
@@ -62,7 +63,7 @@ constructor(
private fun subscribeToRotation() {
try {
- displayManager.registerDisplayListener(displayListener, mainHandler)
+ displayManager.registerDisplayListener(displayListener, handler)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
@@ -100,4 +101,10 @@ constructor(
override fun onDisplayRemoved(displayId: Int) {}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a new [RotationChangeProvider] that provides updated using [handler]. */
+ fun create(handler: Handler): RotationChangeProvider
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index 89fb12e313ec..14c4cc0aeecc 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -18,21 +18,26 @@ import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
+import android.os.Handler
import android.os.Trace
import androidx.core.util.Consumer
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
-import javax.inject.Inject
internal class HingeSensorAngleProvider
-@Inject
+@AssistedInject
constructor(
private val sensorManager: SensorManager,
- @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor
+ @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor,
+ @Assisted private val listenerHandler: Handler,
) : HingeAngleProvider {
private val sensorListener = HingeAngleSensorListener()
- private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+ private val listeners: MutableList<Consumer<Float>> = CopyOnWriteArrayList()
var started = false
override fun start() {
@@ -43,7 +48,8 @@ constructor(
sensorManager.registerListener(
sensorListener,
sensor,
- SensorManager.SENSOR_DELAY_FASTEST
+ SensorManager.SENSOR_DELAY_FASTEST,
+ listenerHandler
)
Trace.endSection()
@@ -75,4 +81,10 @@ constructor(
listeners.forEach { it.accept(event.values[0]) }
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates an [HingeSensorAngleProvider] that sends updates using [handler]. */
+ fun create(handler: Handler): HingeSensorAngleProvider
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
index d8bc01804f14..a31896a96a2b 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -16,7 +16,9 @@ package com.android.systemui.unfold.util
import android.os.Trace
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import javax.inject.Qualifier
/**
@@ -26,11 +28,11 @@ import javax.inject.Qualifier
* for each fold/unfold: in (1) systemui and (2) launcher process.
*/
class ATraceLoggerTransitionProgressListener
-@Inject
-internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
+@AssistedInject
+internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String, @Assisted details: String) :
TransitionProgressListener {
- private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
+ private val traceName = "$tracePrefix$details#$UNFOLD_TRANSITION_TRACE_NAME"
override fun onTransitionStarted() {
Trace.beginAsyncSection(traceName, /* cookie= */ 0)
@@ -43,6 +45,12 @@ internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
override fun onTransitionProgress(progress: Float) {
Trace.setCounter(traceName, (progress * 100).toLong())
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates an [ATraceLoggerTransitionProgressListener] with [details] in the track name. */
+ fun create(details: String): ATraceLoggerTransitionProgressListener
+ }
}
private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index c70c171048e7..79bfa443b330 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -1,5 +1,8 @@
# Ravenwood "policy" file for framework-minus-apex.
+# Keep all AIDL interfaces
+class :aidl stubclass
+
# Collections
class android.util.ArrayMap stubclass
class android.util.ArraySet stubclass
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 74538ac02289..6cac6a47c77b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -668,7 +668,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationConnectionManager(), mAms.getTraceManager(),
mAms.getMagnificationController(),
detectControlGestures,
detectTwoFingerTripleTap,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b5e8c849517b..2eecb4d3a86c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -158,10 +158,10 @@ import com.android.internal.util.Preconditions;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.MagnificationScaleProvider;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -3442,7 +3442,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
- getWindowMagnificationMgr().requestConnection(connect);
+ getMagnificationConnectionManager().requestConnection(connect);
}
/**
@@ -4122,17 +4122,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR_SERVICE);
- getWindowMagnificationMgr().setConnection(connection);
+ getMagnificationConnectionManager().setConnection(connection);
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return WindowMagnificationManager
+ * @return MagnificationManager
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- return mMagnificationController.getWindowMagnificationMgr();
+ return mMagnificationController.getMagnificationConnectionManager();
}
}
@@ -4423,7 +4423,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.println();
}
pw.append("hasWindowMagnificationConnection=").append(
- String.valueOf(getWindowMagnificationMgr().isConnected()));
+ String.valueOf(getMagnificationConnectionManager().isConnected()));
pw.println();
mMagnificationProcessor.dump(pw, getValidDisplayList());
final int userCount = mUserStates.size();
@@ -5144,7 +5144,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = 0; i < displays.size(); i++) {
final int displayId = displays.get(i).getDisplayId();
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
}
}
@@ -5580,6 +5580,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void injectInputEventToInputFilter(InputEvent event) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.INJECT_EVENTS,
+ "injectInputEventToInputFilter");
synchronized (mLock) {
final long endMillis =
SystemClock.uptimeMillis() + WAIT_INPUT_FILTER_INSTALL_TIMEOUT_MS;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d31b1efafcc4..e3797c98bebe 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -258,6 +258,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
public void logMagnificationTripleTap(boolean enabled) {
AccessibilityStatsLogUtils.logMagnificationTripleTap(enabled);
}
+
+ @Override
+ public void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ AccessibilityStatsLogUtils.logMagnificationTwoFingerTripleTap(enabled);
+ }
};
}
@@ -419,6 +424,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
/** An interface that allows testing magnification log events. */
interface MagnificationLogger {
void logMagnificationTripleTap(boolean enabled);
+ void logMagnificationTwoFingerTripleTap(boolean enabled);
}
interface State {
@@ -987,12 +993,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
- } else if (isMultiTapTriggered(3 /* taps */)) {
- onTripleTap(/* up */ event);
-
} else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ // Placing multiple fingers before a single finger, because achieving a
+ // multi finger multi tap also means achieving a single finger triple tap
onTripleTap(event);
+ } else if (isMultiTapTriggered(3 /* taps */)) {
+ onTripleTap(/* up */ event);
+
} else if (
// Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
isFingerDown()
@@ -1026,6 +1034,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mCompletedTapCount++;
mIsTwoFingerCountReached = false;
}
+
+ if (mDetectTwoFingerTripleTap && mCompletedTapCount > 2) {
+ final boolean enabled = !isActivated();
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ }
return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
}
@@ -1037,6 +1050,29 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
+
+ void transitionToViewportDraggingStateAndClear(MotionEvent down) {
+
+ if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
+ final boolean shortcutTriggered = mShortcutTriggered;
+
+ // Only log the 3tap and hold event
+ if (!shortcutTriggered) {
+ final boolean enabled = !isActivated();
+ if (mCompletedTapCount == 2) {
+ // Two finger triple tap and hold
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ } else {
+ // Triple tap and hold also belongs to triple tap event
+ mMagnificationLogger.logMagnificationTripleTap(enabled);
+ }
+ }
+ clear();
+
+ mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
+ zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
+ transitionTo(mViewportDraggingState);
+ }
}
/**
@@ -1416,8 +1452,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// Only log the 3tap and hold event
if (!shortcutTriggered) {
- // TODO:(b/309534286): Add metrics for two-finger triple-tap and fix
- // the log two-finger bug before enabling the flag
// Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 3ea805bbb4a6..5a3c070819bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -60,19 +60,19 @@ import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
- * A class to manipulate window magnification through {@link MagnificationConnectionWrapper}
+ * A class to manipulate magnification through {@link MagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
* SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
* The applied magnification scale is constrained by
* {@link MagnificationScaleProvider#constrainScale(float)}
*/
-public class WindowMagnificationManager implements
+public class MagnificationConnectionManager implements
PanningScalingHandler.MagnificationDelegate,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DBG = false;
- private static final String TAG = "WindowMagnificationMgr";
+ private static final String TAG = "MagnificationConnectionManager";
/**
* Indicate that the magnification window is at the magnification center.
@@ -208,7 +208,7 @@ public class WindowMagnificationManager implements
private final AccessibilityTraceManager mTrace;
private final MagnificationScaleProvider mScaleProvider;
- public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
+ public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback,
AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
mLock = lock;
@@ -1040,7 +1040,7 @@ public class WindowMagnificationManager implements
private float mScale = MagnificationScaleProvider.MIN_SCALE;
private boolean mEnabled;
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
// Records the bounds of window magnification.
private final Rect mBounds = new Rect();
// The magnified bounds on the screen.
@@ -1058,11 +1058,15 @@ public class WindowMagnificationManager implements
"mTrackingTypingFocusSumTime");
private volatile long mTrackingTypingFocusSumTime = 0;
- WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
+ WindowMagnifier(int displayId,
+ MagnificationConnectionManager magnificationConnectionManager) {
mDisplayId = displayId;
- mWindowMagnificationManager = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#enableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition, int id) {
@@ -1072,8 +1076,8 @@ public class WindowMagnificationManager implements
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
- if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, mMagnificationFrameOffsetRatio.x,
+ if (mMagnificationConnectionManager.enableWindowMagnificationInternal(mDisplayId,
+ normScale, centerX, centerY, mMagnificationFrameOffsetRatio.x,
mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
@@ -1096,12 +1100,15 @@ public class WindowMagnificationManager implements
}
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#disableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
if (!mEnabled) {
return false;
}
- if (mWindowMagnificationManager.disableWindowMagnificationInternal(
+ if (mMagnificationConnectionManager.disableWindowMagnificationInternal(
mDisplayId, animationResultCallback)) {
mEnabled = false;
mIdOfLastServiceToControl = INVALID_SERVICE_ID;
@@ -1112,6 +1119,10 @@ public class WindowMagnificationManager implements
return false;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#setScaleInternal should
+ // be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one as
+ // 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void setScale(float scale) {
if (!mEnabled) {
@@ -1119,7 +1130,8 @@ public class WindowMagnificationManager implements
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (Float.compare(mScale, normScale) != 0
- && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
+ && mMagnificationConnectionManager
+ .setScaleForWindowMagnificationInternal(mDisplayId, scale)) {
mScale = normScale;
}
}
@@ -1159,8 +1171,8 @@ public class WindowMagnificationManager implements
}
void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
- if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId)
- && mWindowMagnificationManager.isImeVisible(mDisplayId)
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
+ && mMagnificationConnectionManager.isImeVisible(mDisplayId)
&& trackingTypingFocusEnabled) {
startTrackingTypingFocusRecord();
}
@@ -1206,7 +1218,7 @@ public class WindowMagnificationManager implements
Slog.d(TAG, "stop and log: session duration = " + duration
+ ", elapsed = " + elapsed);
}
- mWindowMagnificationManager.logTrackingTypingFocus(duration);
+ mMagnificationConnectionManager.logTrackingTypingFocus(duration);
mTrackingTypingFocusStartTime = 0;
mTrackingTypingFocusSumTime = 0;
}
@@ -1216,9 +1228,14 @@ public class WindowMagnificationManager implements
return mEnabled;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#moveWindowMagnifierInternal
+ // should be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one
+ // as 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void move(float offsetX, float offsetY) {
- mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY);
+ mMagnificationConnectionManager.moveWindowMagnifierInternal(
+ mDisplayId, offsetX, offsetY);
}
@GuardedBy("mLock")
@@ -1270,8 +1287,10 @@ public class WindowMagnificationManager implements
animationCallback);
}
- private boolean setScaleInternal(int displayId, float scale) {
- return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
+ @GuardedBy("mLock")
+ private boolean setScaleForWindowMagnificationInternal(int displayId, float scale) {
+ return mConnectionWrapper != null
+ && mConnectionWrapper.setScaleForWindowMagnification(displayId, scale);
}
@GuardedBy("mLock")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index f0c44d64f5ec..20538f167656 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -82,16 +82,16 @@ class MagnificationConnectionWrapper {
return true;
}
- boolean setScale(int displayId, float scale) {
+ boolean setScaleForWindowMagnification(int displayId, float scale) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale);
}
try {
- mConnection.setScale(displayId, scale);
+ mConnection.setScaleForWindowMagnification(displayId, scale);
} catch (RemoteException e) {
if (DBG) {
- Slog.e(TAG, "Error calling setScale()", e);
+ Slog.e(TAG, "Error calling setScaleForWindowMagnification()", e);
}
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index effd8732d086..52e123a5e70c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -77,7 +77,7 @@ import java.util.concurrent.Executor;
* <b>Note</b> Updates magnification switch UI when magnification mode transition
* is done and before invoking {@link TransitionCallBack#onResult}.
*/
-public class MagnificationController implements WindowMagnificationManager.Callback,
+public class MagnificationController implements MagnificationConnectionManager.Callback,
MagnificationGestureHandler.Callback,
FullScreenMagnificationController.MagnificationInfoChangedCallback,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
@@ -96,7 +96,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
private final MagnificationScaleProvider mScaleProvider;
private FullScreenMagnificationController mFullScreenMagnificationController;
- private WindowMagnificationManager mWindowMagnificationMgr;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
@@ -164,11 +164,11 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@VisibleForTesting
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, FullScreenMagnificationController fullScreenMagnificationController,
- WindowMagnificationManager windowMagnificationManager,
+ MagnificationConnectionManager magnificationConnectionManager,
MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
this(ams, lock, context, scaleProvider, backgroundExecutor);
mFullScreenMagnificationController = fullScreenMagnificationController;
- mWindowMagnificationMgr = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
@Override
@@ -179,10 +179,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (updatePersistence) {
getFullScreenMagnificationController().persistScale(displayId);
}
- } else if (getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId)) {
- getWindowMagnificationMgr().setScale(displayId, scale);
+ } else if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
+ getMagnificationConnectionManager().setScale(displayId, scale);
if (updatePersistence) {
- getWindowMagnificationMgr().persistScale(displayId);
+ getMagnificationConnectionManager().persistScale(displayId);
}
}
}
@@ -222,15 +222,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
if (showModeSwitchButton) {
- getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+ getMagnificationConnectionManager().showMagnificationButton(displayId, mode);
} else {
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
if (!enableSettingsPanel) {
// Whether the settings panel needs to be shown is controlled in system UI.
// Here, we only guarantee that the settings panel is closed when it is not needed.
- getWindowMagnificationMgr().removeMagnificationSettingsPanel(displayId);
+ getMagnificationConnectionManager().removeMagnificationSettingsPanel(displayId);
}
}
@@ -284,7 +284,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float scale = getTargetModeScaleFromCurrentMagnification(displayId, targetMode);
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -295,7 +296,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, animationEndCallback);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ magnificationConnectionManager.disableWindowMagnification(displayId, false,
animationEndCallback);
}
}
@@ -340,7 +341,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float targetScale = Float.isNaN(config.getScale())
? getTargetModeScaleFromCurrentMagnification(displayId, targetMode)
: config.getScale();
@@ -353,14 +355,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (targetMode == MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
if (targetActivated) {
- windowMagnificationMgr.enableWindowMagnification(displayId,
+ magnificationConnectionManager.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
magnificationAnimationCallback, id);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
} else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) {
- windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
+ magnificationConnectionManager.disableWindowMagnification(
+ displayId, false, null);
if (targetActivated) {
if (!screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.register(displayId);
@@ -409,7 +412,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
return getFullScreenMagnificationController().getScale(displayId);
} else {
- return getWindowMagnificationMgr().getScale(displayId);
+ return getMagnificationConnectionManager().getScale(displayId);
}
}
@@ -441,7 +444,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mAccessibilityCallbacksDelegateArray.put(displayId,
getFullScreenMagnificationController());
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
- mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr());
+ mAccessibilityCallbacksDelegateArray.put(
+ displayId, getMagnificationConnectionManager());
} else {
mAccessibilityCallbacksDelegateArray.delete(displayId);
}
@@ -462,13 +466,13 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@Override
public void onRequestMagnificationSpec(int displayId, int serviceId) {
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
updateMagnificationUIControls(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
}
- if (windowMagnificationManager != null) {
- mWindowMagnificationMgr.disableWindowMagnification(displayId, false);
+ if (magnificationConnectionManager != null) {
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -491,7 +495,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
setCurrentMagnificationModeAndSwitchDelegate(displayId,
ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
- scale = mWindowMagnificationMgr.getLastActivatedScale(displayId);
+ scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
}
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
}
@@ -507,13 +511,14 @@ public class MagnificationController implements WindowMagnificationManager.Callb
public void onSourceBoundsChanged(int displayId, Rect bounds) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) {
// notify sysui the magnification scale changed on window magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
- mUserId, displayId, getWindowMagnificationMgr().getScale(displayId));
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+ mUserId, displayId, getMagnificationConnectionManager().getScale(displayId));
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
- .setActivated(getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId))
- .setScale(getWindowMagnificationMgr().getScale(displayId))
+ .setActivated(
+ getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId))
+ .setScale(getMagnificationConnectionManager().getScale(displayId))
.setCenterX(bounds.exactCenterX())
.setCenterY(bounds.exactCenterY()).build();
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
@@ -525,7 +530,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@NonNull MagnificationConfig config) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) {
// notify sysui the magnification scale changed on fullscreen magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
mUserId, displayId, config.getScale());
mAms.notifyMagnificationChanged(displayId, region, config);
@@ -548,8 +553,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
final boolean fullScreenActivated = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isActivated(displayId);
- final boolean windowEnabled = mWindowMagnificationMgr != null
- && mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ final boolean windowEnabled = mMagnificationConnectionManager != null
+ && mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
final Integer transitionMode = mTransitionModes.get(displayId);
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenActivated)
|| (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
@@ -608,10 +613,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
private void disableWindowMagnificationIfNeeded(int displayId) {
- final WindowMagnificationManager windowMagnificationManager =
- getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
if (isActivated(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
- windowMagnificationManager.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -620,7 +625,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
mIsImeVisibleArray.put(displayId, shown);
}
- getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown);
+ getMagnificationConnectionManager().onImeWindowVisibilityChanged(displayId, shown);
logMagnificationModeWithImeOnIfNeeded(displayId);
}
@@ -661,7 +666,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
/**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
- * WindowMagnificationManager}.
+ * MagnificationConnectionManager}.
*
* @param userId the currently active user ID
*/
@@ -671,10 +676,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
mUserId = userId;
final FullScreenMagnificationController fullMagnificationController;
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
fullMagnificationController = mFullScreenMagnificationController;
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
mAccessibilityCallbacksDelegateArray.clear();
mCurrentMagnificationModeArray.clear();
mLastMagnificationActivatedModeArray.clear();
@@ -685,8 +690,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (fullMagnificationController != null) {
fullMagnificationController.resetAllIfNeeded(false);
}
- if (windowMagnificationManager != null) {
- windowMagnificationManager.disableAllWindowMagnifiers();
+ if (magnificationConnectionManager != null) {
+ magnificationConnectionManager.disableAllWindowMagnifiers();
}
}
@@ -700,8 +705,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (mFullScreenMagnificationController != null) {
mFullScreenMagnificationController.onDisplayRemoved(displayId);
}
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ if (mMagnificationConnectionManager != null) {
+ mMagnificationConnectionManager.onDisplayRemoved(displayId);
}
mAccessibilityCallbacksDelegateArray.delete(displayId);
mCurrentMagnificationModeArray.delete(displayId);
@@ -728,7 +733,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
* @param enabled Enable the following typing focus feature
*/
public void setMagnificationFollowTypingEnabled(boolean enabled) {
- getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+ getMagnificationConnectionManager().setMagnificationFollowTypingEnabled(enabled);
getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
}
@@ -805,29 +810,29 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return {@link WindowMagnificationManager}.
+ * @return {@link MagnificationConnectionManager}.
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ if (mMagnificationConnectionManager == null) {
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
mLock, this, mAms.getTraceManager(),
mScaleProvider);
}
- return mWindowMagnificationMgr;
+ return mMagnificationConnectionManager;
}
}
private @Nullable PointF getCurrentMagnificationCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- if (mWindowMagnificationMgr == null
- || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
+ if (mMagnificationConnectionManager == null
+ || !mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
- mWindowMagnificationMgr.getCenterY(displayId));
+ mTempPoint.set(mMagnificationConnectionManager.getCenterX(displayId),
+ mMagnificationConnectionManager.getCenterY(displayId));
} else {
if (mFullScreenMagnificationController == null
|| !mFullScreenMagnificationController.isActivated(displayId)) {
@@ -858,10 +863,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
+ if (mMagnificationConnectionManager == null) {
return false;
}
- isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ isActivated = mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
}
return isActivated;
@@ -980,7 +985,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mCurrentCenter.x, mCurrentCenter.y, mAnimate,
MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
+ getMagnificationConnectionManager().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null,
MAGNIFICATION_GESTURE_HANDLER_ID);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 5cf2a638fa3e..ed8f1ab3a1b2 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -84,13 +84,13 @@ public class MagnificationProcessor {
.setCenterX(fullScreenMagnificationController.getCenterX(displayId))
.setCenterY(fullScreenMagnificationController.getCenterY(displayId));
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- final WindowMagnificationManager windowMagnificationManager =
- mController.getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ mController.getMagnificationConnectionManager();
builder.setMode(mode)
.setActivated(mController.isActivated(displayId, MAGNIFICATION_MODE_WINDOW))
- .setScale(windowMagnificationManager.getScale(displayId))
- .setCenterX(windowMagnificationManager.getCenterX(displayId))
- .setCenterY(windowMagnificationManager.getCenterY(displayId));
+ .setScale(magnificationConnectionManager.getScale(displayId))
+ .setCenterX(magnificationConnectionManager.getCenterX(displayId))
+ .setCenterY(magnificationConnectionManager.getCenterY(displayId));
} else {
// For undefined mode, set enabled to false
builder.setActivated(false);
@@ -135,12 +135,12 @@ public class MagnificationProcessor {
}
} else if (configMode == MAGNIFICATION_MODE_WINDOW) {
if (configActivated) {
- return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
- config.getScale(), config.getCenterX(), config.getCenterY(),
+ return mController.getMagnificationConnectionManager().enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY(),
animate ? STUB_ANIMATION_CALLBACK : null,
id);
} else {
- return mController.getWindowMagnificationMgr()
+ return mController.getMagnificationConnectionManager()
.disableWindowMagnification(displayId, false);
}
}
@@ -256,7 +256,7 @@ public class MagnificationProcessor {
if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
} else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ mController.getMagnificationConnectionManager().getMagnificationSourceBounds(displayId,
outRegion);
}
}
@@ -297,8 +297,8 @@ public class MagnificationProcessor {
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
- false, animate ? STUB_ANIMATION_CALLBACK : null);
+ return mController.getMagnificationConnectionManager().disableWindowMagnification(
+ displayId, false, animate ? STUB_ANIMATION_CALLBACK : null);
}
return false;
}
@@ -325,19 +325,20 @@ public class MagnificationProcessor {
*/
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
- mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
+ mController.getMagnificationConnectionManager().resetAllIfNeeded(connectionId);
}
/**
* {@link FullScreenMagnificationController#isActivated(int)}
- * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
+ * {@link MagnificationConnectionManager#isWindowMagnifierEnabled(int)}
*/
public boolean isMagnifying(int displayId) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().isActivated(displayId);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+ return mController.getMagnificationConnectionManager().isWindowMagnifierEnabled(
+ displayId);
}
return false;
}
@@ -416,22 +417,23 @@ public class MagnificationProcessor {
pw.append(" SupportWindowMagnification="
+ mController.supportWindowMagnification()).println();
pw.append(" WindowMagnificationConnectionState="
- + mController.getWindowMagnificationMgr().getConnectionState()).println();
+ + mController.getMagnificationConnectionManager().getConnectionState()).println();
}
private int getIdOfLastServiceToMagnify(int mode, int displayId) {
return (mode == MAGNIFICATION_MODE_FULLSCREEN)
? mController.getFullScreenMagnificationController()
.getIdOfLastServiceToMagnify(displayId)
- : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+ : mController.getMagnificationConnectionManager().getIdOfLastServiceToMagnify(
displayId);
}
private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
int mode) {
if (mode == MAGNIFICATION_MODE_WINDOW) {
- pw.append(" TrackingTypingFocusEnabled=" + mController
- .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
+ pw.append(" TrackingTypingFocusEnabled="
+ + mController.getMagnificationConnectionManager()
+ .isTrackingTypingFocusEnabled(displayId))
.println();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 94556282d0d3..6b48d2bacf9d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -45,6 +45,7 @@ class PanningScalingHandler extends
private static final String TAG = "PanningScalingHandler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // TODO(b/312372035): Revisit the scope of usage of the interface
interface MagnificationDelegate {
boolean processScroll(int displayId, float distanceX, float distanceY);
void setScale(int displayId, float scale);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 36e751181cfd..73c267a6e262 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -79,7 +79,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
- private final WindowMagnificationManager mWindowMagnificationMgr;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
@VisibleForTesting
final DelegatingState mDelegatingState;
@VisibleForTesting
@@ -101,7 +101,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
private long mTripleTapAndHoldStartedTime = 0;
public WindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap,
@@ -115,7 +115,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
}
mContext = context;
- mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationConnectionManager = magnificationConnectionManager;
mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
(event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
policyFlags));
@@ -128,18 +128,18 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@Override
public boolean processScroll(int displayId, float distanceX,
float distanceY) {
- return mWindowMagnificationMgr.processScroll(displayId, distanceX,
- distanceY);
+ return mMagnificationConnectionManager.processScroll(
+ displayId, distanceX, distanceY);
}
@Override
public void setScale(int displayId, float scale) {
- mWindowMagnificationMgr.setScale(displayId, scale);
+ mMagnificationConnectionManager.setScale(displayId, scale);
}
@Override
public float getScale(int displayId) {
- return mWindowMagnificationMgr.getScale(displayId);
+ return mMagnificationConnectionManager.getScale(displayId);
}
}));
@@ -167,7 +167,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
Slog.i(mLogTag, "onDestroy(); delayed = "
+ mDetectingState.toString());
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
resetToDetectState();
}
@@ -176,7 +176,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
final Point screenSize = mTempPoint;
getScreenSize(mTempPoint);
toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
private void getScreenSize(Point outSize) {
@@ -190,28 +190,29 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
}
private void enableWindowMagnifier(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
if (DEBUG_ALL) {
Slog.i(mLogTag, "enableWindowMagnifier :"
+ centerX + ", " + centerY + ", " + windowPosition);
}
final float scale = MathUtils.constrain(
- mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
- mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
- windowPosition);
+ mMagnificationConnectionManager.getPersistedScale(mDisplayId),
+ MIN_SCALE, MAX_SCALE);
+ mMagnificationConnectionManager.enableWindowMagnification(
+ mDisplayId, scale, centerX, centerY, windowPosition);
}
private void disableWindowMagnifier() {
if (DEBUG_ALL) {
Slog.i(mLogTag, "disableWindowMagnifier()");
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, false);
}
private void toggleMagnification(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
- if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)) {
disableWindowMagnifier();
} else {
enableWindowMagnifier(centerX, centerY, windowPosition);
@@ -223,7 +224,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
Slog.i(mLogTag, "onTripleTap()");
}
toggleMagnification(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
@VisibleForTesting
@@ -232,9 +233,9 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
Slog.i(mLogTag, "onTripleTapAndHold()");
}
mViewportDraggingState.mEnabledBeforeDrag =
- mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
+ mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId);
enableWindowMagnifier(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
transitionTo(mViewportDraggingState);
}
@@ -242,7 +243,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@VisibleForTesting
void releaseTripleTapAndHold() {
if (!mViewportDraggingState.mEnabledBeforeDrag) {
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
}
transitionTo(mDetectingState);
if (mTripleTapAndHoldStartedTime != 0) {
@@ -331,7 +332,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@Override
public void onExit() {
mPanningScalingHandler.setEnabled(false);
- mWindowMagnificationMgr.persistScale(mDisplayId);
+ mMagnificationConnectionManager.persistScale(mDisplayId);
clear();
}
@@ -404,7 +405,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
float offsetX = event.getX() - mLastX;
float offsetY = event.getY() - mLastY;
- mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
+ mMagnificationConnectionManager.moveWindowMagnification(mDisplayId, offsetX,
offsetY);
}
mLastX = event.getX();
@@ -522,7 +523,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
- return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
+ return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
&& !mDetectSingleFingerTripleTap
&& !(mDetectTwoFingerTripleTap
&& Flags.enableMagnificationMultipleFingerMultipleTapGesture());
@@ -540,7 +541,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
"onGestureDetected : delayedEventQueue = " + delayedEventQueue);
}
if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
- && mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
+ && mMagnificationConnectionManager
+ .pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
onTripleTap(motionEvent);
@@ -584,7 +586,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
+ ", mMagnifiedInteractionState=" + mObservePanningScalingState
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
- + ", mWindowMagnificationMgr=" + mWindowMagnificationMgr
+ + ", mMagnificationConnectionManager=" + mMagnificationConnectionManager
+ ", mDisplayId=" + mDisplayId
+ '}';
}
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 3583a7816ab2..a159a5e003c9 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -55,5 +55,15 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsVirtualDevicesCameraTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
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 e6bfeb79fafb..45d7314f3fab 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -39,7 +39,6 @@ import android.app.Activity;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
-import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -55,8 +54,6 @@ import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -81,7 +78,6 @@ import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualTouchEvent;
import android.hardware.input.VirtualTouchscreenConfig;
import android.os.Binder;
-import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -122,22 +118,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private static final String TAG = "VirtualDeviceImpl";
- /**
- * Virtual displays created by a {@code VirtualDeviceManager.VirtualDevice} are more consistent
- * with virtual displays created via {@link android.hardware.display.DisplayManager} and allow
- * for the creation of private, auto-mirror, and fixed orientation displays since
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}.
- *
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final long MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER =
- 294837146L;
-
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
| DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
@@ -365,8 +345,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
- if (!CompatChanges.isChangeEnabled(
- MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER, mOwnerUid)) {
+ if (!Flags.consistentDisplayFlags()) {
flags |= DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC;
}
if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
@@ -960,7 +939,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.registerCamera(cameraConfig);
}
@Override // Binder call
@@ -972,7 +951,19 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.unregisterCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.unregisterCamera(cameraConfig);
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public int getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ super.getVirtualCameraId_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
+ if (mVirtualCameraController == null) {
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
+ }
+ return mVirtualCameraController.getCameraId(cameraConfig);
}
@Override
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 d194bb29b19b..9b78ed41206d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -460,11 +460,11 @@ public class VirtualDeviceManagerService extends SystemService {
synchronized (mVirtualDeviceManagerLock) {
if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
- final long callindId = Binder.clearCallingIdentity();
+ final long callingId = Binder.clearCallingIdentity();
try {
registerCdmAssociationListener();
} finally {
- Binder.restoreCallingIdentity(callindId);
+ Binder.restoreCallingIdentity(callingId);
}
}
mVirtualDevices.put(deviceId, virtualDevice);
@@ -498,13 +498,16 @@ public class VirtualDeviceManagerService extends SystemService {
synchronized (mVirtualDeviceManagerLock) {
virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
if (virtualDeviceImpl == null) {
- throw new SecurityException("Invalid VirtualDevice");
+ throw new SecurityException(
+ "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
+ + ")");
}
}
if (virtualDeviceImpl.getOwnerUid() != callingUid) {
throw new SecurityException(
"uid " + callingUid
- + " is not the owner of the supplied VirtualDevice");
+ + " is not the owner of the supplied VirtualDevice (deviceId = "
+ + virtualDevice.getDeviceId() + ")");
}
return virtualDeviceImpl.createVirtualDisplay(
@@ -851,6 +854,11 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public int getDeviceIdForDisplayId(int displayId) {
+ return mImpl.getDeviceIdForDisplayId(displayId);
+ }
+
+ @Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 06be3f39dcd1..d089b05238e4 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -27,14 +27,14 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Set;
+import java.util.Map;
/**
* Manages the registration and removal of virtual camera from the server side.
@@ -47,10 +47,13 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
private static final String TAG = "VirtualCameraController";
+ private final Object mServiceLock = new Object();
+
+ @GuardedBy("mServiceLock")
@Nullable private IVirtualCameraService mVirtualCameraService;
@GuardedBy("mCameras")
- private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
+ private final Map<IBinder, CameraDescriptor> mCameras = new ArrayMap<>();
public VirtualCameraController() {
connectVirtualCameraService();
@@ -67,19 +70,16 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
- // Try to connect to service if not connected already.
- if (mVirtualCameraService == null) {
- connectVirtualCameraService();
- }
- // Throw exception if we are unable to connect to service.
- if (mVirtualCameraService == null) {
- throw new IllegalStateException("Virtual camera service is not connected.");
- }
+ connectVirtualCameraServiceIfNeeded();
try {
if (registerCameraWithService(cameraConfig)) {
+ CameraDescriptor cameraDescriptor =
+ new CameraDescriptor(cameraConfig);
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ binder.linkToDeath(cameraDescriptor, 0 /* flags */);
synchronized (mCameras) {
- mCameras.add(cameraConfig);
+ mCameras.put(binder, cameraDescriptor);
}
} else {
// TODO(b/310857519): Revisit this to find a better way of indicating failure.
@@ -96,24 +96,44 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
- try {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
+ synchronized (mCameras) {
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ if (!mCameras.containsKey(binder)) {
+ Slog.w(TAG, "Virtual camera was not registered.");
} else {
- mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ mVirtualCameraService.unregisterCamera(binder);
+ }
+ mCameras.remove(binder);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
- synchronized (mCameras) {
- mCameras.remove(cameraConfig);
+ }
+ }
+
+ /** Return the id of the virtual camera with the given config. */
+ public int getCameraId(@NonNull VirtualCameraConfig cameraConfig) {
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder());
}
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@Override
public void binderDied() {
Slog.d(TAG, "Virtual camera service died.");
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
synchronized (mCameras) {
mCameras.clear();
}
@@ -122,32 +142,48 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
/** Release resources associated with this controller. */
public void close() {
synchronized (mCameras) {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
- } else {
- for (VirtualCameraConfig config : mCameras) {
- try {
- mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
- } catch (RemoteException e) {
- Slog.w(TAG, "close(): Camera failed to be removed on camera "
- + "service.", e);
+ if (!mCameras.isEmpty()) {
+ connectVirtualCameraServiceIfNeeded();
+
+ synchronized (mServiceLock) {
+ for (IBinder binder : mCameras.keySet()) {
+ try {
+ mVirtualCameraService.unregisterCamera(binder);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "close(): Camera failed to be removed on camera "
+ + "service.", e);
+ }
}
}
+ mCameras.clear();
}
- mCameras.clear();
}
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
}
/** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
public void dump(PrintWriter fout, String indent) {
fout.println(indent + "VirtualCameraController:");
indent += indent;
- fout.printf("%sService:%s\n", indent, mVirtualCameraService);
synchronized (mCameras) {
fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
- for (VirtualCameraConfig config : mCameras) {
- fout.printf("%s token: %s\n", indent, config);
+ for (CameraDescriptor descriptor : mCameras.values()) {
+ fout.printf("%s token: %s\n", indent, descriptor.mConfig);
+ }
+ }
+ }
+
+ private void connectVirtualCameraServiceIfNeeded() {
+ synchronized (mServiceLock) {
+ // Try to connect to service if not connected already.
+ if (mVirtualCameraService == null) {
+ connectVirtualCameraService();
+ }
+ // Throw exception if we are unable to connect to service.
+ if (mVirtualCameraService == null) {
+ throw new IllegalStateException("Virtual camera service is not connected.");
}
}
}
@@ -173,7 +209,24 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
- return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
- serviceConfiguration);
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+ serviceConfiguration);
+ }
+ }
+
+ private final class CameraDescriptor implements IBinder.DeathRecipient {
+
+ private final VirtualCameraConfig mConfig;
+
+ CameraDescriptor(VirtualCameraConfig config) {
+ mConfig = config;
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Virtual camera binder died");
+ unregisterCamera(mConfig);
+ }
}
}
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index fc565111dbe8..23c008eb7f67 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -27,6 +27,7 @@ import android.content.ComponentName;
import android.content.LocusId;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -134,6 +135,18 @@ public abstract class UsageStatsManagerInternal {
@Nullable LocusId locusId, @NonNull IBinder appToken);
/**
+ * Report a user interaction event to UsageStatsManager
+ *
+ * @param pkgName The package for which this user interaction event occurred.
+ * @param userId The user id to which component belongs to.
+ * @param extras The extra details about this user interaction event.
+ * {@link UsageEvents.Event#USER_INTERACTION}
+ * {@link UsageStatsManager#reportUserInteraction(String, int, PersistableBundle)}
+ */
+ public abstract void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras);
+
+ /**
* Prepares the UsageStatsService for shutdown.
*/
public abstract void prepareShutdown();
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 77b6d583808c..eb3ec2444210 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1466,8 +1466,11 @@ public class BinaryTransparencyService extends SystemService {
if (android.security.Flags.binaryTransparencySepolicyHash()) {
byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes(
"/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer());
- String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
- Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ String sepolicyHashEncoded = null;
+ if (sepolicyHash != null) {
+ sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
+ Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ }
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED,
sepolicyHashEncoded, mVbmetaDigest);
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 987507fe7f03..c258370dc12b 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
# Connectivity / Networking
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
@@ -35,7 +35,7 @@ per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
-per-file PinnerService.java = file:/apct-tests/perftests/OWNERS
+per-file PinnerService.java = file:/core/java/android/app/pinner/OWNERS
per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com
per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 0e7b4aa32b44..c5c2b0b5dd59 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,9 @@ import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
import static android.app.ActivityManager.UID_OBSERVER_GONE;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.flags.Flags.pinWebview;
+
+import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +31,8 @@ import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.SearchManager;
import android.app.UidObserver;
+import android.app.pinner.IPinnerService;
+import android.app.pinner.PinnedFileStat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -48,6 +53,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -83,6 +89,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -111,16 +119,15 @@ public final class PinnerService extends SystemService {
private static final int KEY_ASSISTANT = 2;
// Pin using pinlist.meta when pinning apps.
- private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
- "pinner.use_pinlist", true);
- // Pin the whole odex/vdex/etc file when pinning apps.
- private static boolean PROP_PIN_ODEX = SystemProperties.getBoolean(
- "pinner.whole_odex", true);
+ private static boolean PROP_PIN_PINLIST =
+ SystemProperties.getBoolean("pinner.use_pinlist", true);
private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
+ public static final String ANON_REGION_STAT_NAME = "[anon]";
+
@IntDef({KEY_CAMERA, KEY_HOME, KEY_ASSISTANT})
@Retention(RetentionPolicy.SOURCE)
public @interface AppKey {}
@@ -135,8 +142,7 @@ public final class PinnerService extends SystemService {
private SearchManager mSearchManager;
/** The list of the statically pinned files. */
- @GuardedBy("this")
- private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
+ @GuardedBy("this") private final ArrayMap<String, PinnedFile> mPinnedFiles = new ArrayMap<>();
/** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
@GuardedBy("this")
@@ -175,6 +181,7 @@ public final class PinnerService extends SystemService {
private final boolean mConfiguredToPinCamera;
private final boolean mConfiguredToPinHome;
private final boolean mConfiguredToPinAssistant;
+ private final int mConfiguredWebviewPinBytes;
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -214,6 +221,11 @@ public final class PinnerService extends SystemService {
protected void publishBinderService(PinnerService service, Binder binderService) {
service.publishBinderService("pinner", binderService);
}
+
+ protected PinnedFile pinFileInternal(String fileToPin,
+ int maxBytesToPin, boolean attemptPinIntrospection) {
+ return PinnerService.pinFileInternal(fileToPin, maxBytesToPin, attemptPinIntrospection);
+ }
}
public PinnerService(Context context) {
@@ -233,6 +245,8 @@ public final class PinnerService extends SystemService {
com.android.internal.R.bool.config_pinnerHomeApp);
mConfiguredToPinAssistant = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerAssistantApp);
+ mConfiguredWebviewPinBytes = context.getResources().getInteger(
+ com.android.internal.R.integer.config_pinnerWebviewPinBytes);
mPinKeys = createPinKeys();
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
@@ -322,7 +336,7 @@ public final class PinnerService extends SystemService {
public List<PinnedFileStats> dumpDataForStatsd() {
List<PinnedFileStats> pinnedFileStats = new ArrayList<>();
synchronized (PinnerService.this) {
- for (PinnedFile pinnedFile : mPinnedFiles) {
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
pinnedFileStats.add(new PinnedFileStats(SYSTEM_UID, pinnedFile));
}
@@ -358,39 +372,17 @@ public final class PinnerService extends SystemService {
com.android.internal.R.array.config_defaultPinnerServiceFiles);
// Continue trying to pin each file even if we fail to pin some of them
for (String fileToPin : filesToPin) {
- PinnedFile pf = pinFile(fileToPin,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
+ PinnedFile pf = mInjector.pinFileInternal(fileToPin, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
if (pf == null) {
Slog.e(TAG, "Failed to pin file = " + fileToPin);
continue;
}
synchronized (this) {
- mPinnedFiles.add(pf);
- }
- if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) {
- // Check whether the runtime has compilation artifacts to pin.
- String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(fileToPin, arch);
- } catch (IOException ioe) { }
- if (files == null) {
- continue;
- }
- for (String file : files) {
- PinnedFile df = pinFile(file,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
- if (df == null) {
- Slog.i(TAG, "Failed to pin ART file = " + file);
- continue;
- }
- synchronized (this) {
- mPinnedFiles.add(df);
- }
- }
+ mPinnedFiles.put(pf.fileName, pf);
}
+ pf.groupName = "system";
+ pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, null);
}
refreshPinAnonConfig();
@@ -487,7 +479,7 @@ public final class PinnerService extends SystemService {
pinnedAppFiles = new ArrayList<>(app.mFiles);
}
for (PinnedFile pinnedFile : pinnedAppFiles) {
- pinnedFile.close();
+ unpinFile(pinnedFile.fileName);
}
}
@@ -495,6 +487,19 @@ public final class PinnerService extends SystemService {
return ResolverActivity.class.getName().equals(info.name);
}
+ public int getWebviewPinQuota() {
+ if (!pinWebview()) {
+ return 0;
+ }
+ int quota = mConfiguredWebviewPinBytes;
+ int overrideQuota = SystemProperties.getInt("pinner.pin_webview_size", -1);
+ if (overrideQuota != -1) {
+ // Quota was overridden
+ quota = overrideQuota;
+ }
+ return quota;
+ }
+
private ApplicationInfo getCameraInfo(int userHandle) {
Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
@@ -728,7 +733,7 @@ public final class PinnerService extends SystemService {
case KEY_ASSISTANT:
return "Assistant";
default:
- return null;
+ return "";
}
}
@@ -868,11 +873,12 @@ public final class PinnerService extends SystemService {
continue;
}
- PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+ PinnedFile pf = mInjector.pinFileInternal(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
if (pf == null) {
Slog.e(TAG, "Failed to pin " + apk);
continue;
}
+ pf.groupName = getNameForKey(key);
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
@@ -882,40 +888,118 @@ public final class PinnerService extends SystemService {
}
apkPinSizeLimit -= pf.bytesPinned;
+ if (apk.equals(appInfo.sourceDir)) {
+ pinOptimizedDexDependencies(pf, apkPinSizeLimit, appInfo);
+ }
}
+ }
- // determine the ABI from either ApplicationInfo or Build
- String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
- Build.SUPPORTED_ABIS[0];
- String arch = VMRuntime.getInstructionSet(abi);
- // get the path to the odex or oat file
- String baseCodePath = appInfo.getBaseCodePath();
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
- } catch (IOException ioe) {}
- if (files == null) {
- return;
+ /**
+ * Pin file or apk to memory.
+ *
+ * Prefer to use this method instead of {@link #pinFileInternal(String, int, boolean)} as it
+ * takes care of accounting and if pinning an apk, it also pins any extra optimized art files
+ * that related to the file but not within itself.
+ *
+ * @param fileToPin File to pin
+ * @param maxBytesToPin maximum quota allowed for pinning
+ * @return total bytes that were pinned.
+ */
+ public int pinFile(String fileToPin, int maxBytesToPin, @Nullable ApplicationInfo appInfo,
+ @Nullable String groupName) {
+ PinnedFile existingPin;
+ synchronized(this) {
+ existingPin = mPinnedFiles.get(fileToPin);
+ }
+ if (existingPin != null) {
+ if (existingPin.bytesPinned == maxBytesToPin) {
+ // Duplicate pin requesting same amount of bytes, lets just bail out.
+ return 0;
+ } else {
+ // User decided to pin a different amount of bytes than currently pinned
+ // so this is a valid pin request. Unpin the previous version before repining.
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinning file prior to repin: " + fileToPin);
+ }
+ unpinFile(fileToPin);
+ }
}
- //not pinning the oat/odex is not a fatal error
- for (String file : files) {
- PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
- if (pf != null) {
- synchronized (this) {
- if (PROP_PIN_ODEX) {
- pinnedApp.mFiles.add(pf);
- }
+ boolean isApk = fileToPin.endsWith(".apk");
+ int bytesPinned = 0;
+ PinnedFile pf = mInjector.pinFileInternal(fileToPin, maxBytesToPin,
+ /*attemptPinIntrospection=*/isApk);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin file = " + fileToPin);
+ return 0;
+ }
+ pf.groupName = groupName != null ? groupName : "";
+
+ maxBytesToPin -= bytesPinned;
+ bytesPinned += pf.bytesPinned;
+
+ synchronized (this) {
+ mPinnedFiles.put(pf.fileName, pf);
+ }
+ if (maxBytesToPin > 0) {
+ pinOptimizedDexDependencies(pf, maxBytesToPin, appInfo);
+ }
+ return bytesPinned;
+ }
+
+ /**
+ * Pin any dependency optimized files generated by ART.
+ * @param pinnedFile An already pinned file whose dependencies we want pinned.
+ * @param maxBytesToPin Maximum amount of bytes to pin.
+ * @param appInfo Used to determine the ABI in case the application has one custom set, when set
+ * to null it will use the default supported ABI by the device.
+ * @return total bytes pinned.
+ */
+ private int pinOptimizedDexDependencies(
+ PinnedFile pinnedFile, int maxBytesToPin, @Nullable ApplicationInfo appInfo) {
+ if (pinnedFile == null) {
+ return 0;
+ }
+
+ int bytesPinned = 0;
+ if (pinnedFile.fileName.endsWith(".jar") | pinnedFile.fileName.endsWith(".apk")) {
+ String abi = null;
+ if (appInfo != null) {
+ abi = appInfo.primaryCpuAbi;
+ }
+ if (abi == null) {
+ abi = Build.SUPPORTED_ABIS[0];
+ }
+ // Check whether the runtime has compilation artifacts to pin.
+ String arch = VMRuntime.getInstructionSet(abi);
+ String[] files = null;
+ try {
+ files = DexFile.getDexFileOutputPaths(pinnedFile.fileName, arch);
+ } catch (IOException ioe) {
+ }
+ if (files == null) {
+ return bytesPinned;
+ }
+ for (String file : files) {
+ // Unpin if it was already pinned prior to re-pinning.
+ unpinFile(file);
+
+ PinnedFile df = mInjector.pinFileInternal(file, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
+ if (df == null) {
+ Slog.i(TAG, "Failed to pin ART file = " + file);
+ return bytesPinned;
}
- if (DEBUG) {
- if (PROP_PIN_ODEX) {
- Slog.i(TAG, "Pinned " + pf.fileName);
- } else {
- Slog.i(TAG, "Pinned [skip] " + pf.fileName);
- }
+ df.groupName = pinnedFile.groupName;
+ pinnedFile.pinnedDeps.add(df);
+ maxBytesToPin -= df.bytesPinned;
+ bytesPinned += df.bytesPinned;
+ synchronized (this) {
+ mPinnedFiles.put(df.fileName, df);
}
}
}
+ return bytesPinned;
}
/** mlock length bytes of fileToPin in memory
@@ -955,9 +1039,11 @@ public final class PinnerService extends SystemService {
* zip in order to extract the
* @return Pinned memory resource owner thing or null on error
*/
- private static PinnedFile pinFile(String fileToPin,
- int maxBytesToPin,
- boolean attemptPinIntrospection) {
+ private static PinnedFile pinFileInternal(
+ String fileToPin, int maxBytesToPin, boolean attemptPinIntrospection) {
+ if (DEBUG) {
+ Slog.d(TAG, "pin file: " + fileToPin + " use-pinlist: " + attemptPinIntrospection);
+ }
ZipFile fileAsZip = null;
InputStream pinRangeStream = null;
try {
@@ -968,13 +1054,15 @@ public final class PinnerService extends SystemService {
if (fileAsZip != null) {
pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
}
-
- Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
-
- PinRangeSource pinRangeSource = (pinRangeStream != null)
- ? new PinRangeSourceStream(pinRangeStream)
- : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
- return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
+ boolean use_pinlist = (pinRangeStream != null);
+ PinRangeSource pinRangeSource = use_pinlist
+ ? new PinRangeSourceStream(pinRangeStream)
+ : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
+ PinnedFile pinnedFile = pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
+ if (pinnedFile != null) {
+ pinnedFile.used_pinlist = use_pinlist;
+ }
+ return pinnedFile;
} finally {
safeClose(pinRangeStream);
safeClose(fileAsZip); // Also closes any streams we've opened
@@ -1013,9 +1101,23 @@ public final class PinnerService extends SystemService {
return null;
}
+ // Looking at root directory is the old behavior but still some apps rely on it so keeping
+ // for backward compatibility. As doing a single item lookup is cheap in the root.
ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
+
+ if (pinMetaEntry == null) {
+ // It is usually within an apk's control to include files in assets/ directory
+ // so this would be the expected point to have the pinlist.meta coming from.
+ // we explicitly avoid doing an exhaustive search because it may be expensive so
+ // prefer to have a good known location to retrieve the file.
+ pinMetaEntry = zipFile.getEntry("assets/" + PIN_META_FILENAME);
+ }
+
InputStream pinMetaStream = null;
if (pinMetaEntry != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found pinlist.meta for " + fileName);
+ }
try {
pinMetaStream = zipFile.getInputStream(pinMetaEntry);
} catch (IOException ex) {
@@ -1024,6 +1126,10 @@ public final class PinnerService extends SystemService {
fileName),
ex);
}
+ } else {
+ Slog.w(TAG,
+ String.format(
+ "Could not find pinlist.meta for \"%s\": pinning as blob", fileName));
}
return pinMetaStream;
}
@@ -1160,6 +1266,49 @@ public final class PinnerService extends SystemService {
}
}
}
+ private List<PinnedFile> getAllPinsForGroup(String group) {
+ List<PinnedFile> filesInGroup;
+ synchronized (this) {
+ filesInGroup = mPinnedFiles.values()
+ .stream()
+ .filter(pf -> pf.groupName.equals(group))
+ .toList();
+ }
+ return filesInGroup;
+ }
+ public void unpinGroup(String group) {
+ List<PinnedFile> pinnedFiles = getAllPinsForGroup(group);
+ for (PinnedFile pf : pinnedFiles) {
+ unpinFile(pf.fileName);
+ }
+ }
+
+ public void unpinFile(String filename) {
+ PinnedFile pinnedFile;
+ synchronized (this) {
+ pinnedFile = mPinnedFiles.get(filename);
+ }
+ if (pinnedFile == null) {
+ // File not pinned, nothing to do.
+ return;
+ }
+ pinnedFile.close();
+ synchronized (this) {
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned file: " + filename);
+ }
+ mPinnedFiles.remove(pinnedFile.fileName);
+ for (PinnedFile dep : pinnedFile.pinnedDeps) {
+ if (dep == null) {
+ continue;
+ }
+ mPinnedFiles.remove(dep.fileName);
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned dependency: " + dep.fileName);
+ }
+ }
+ }
+ }
private static int clamp(int min, int value, int max) {
return Math.max(min, Math.min(value, max));
@@ -1205,17 +1354,44 @@ public final class PinnerService extends SystemService {
}
}
- private final class BinderService extends Binder {
+ public List<PinnedFileStat> getPinnerStats() {
+ ArrayList<PinnedFileStat> stats = new ArrayList<>();
+ Collection<PinnedApp> pinnedApps;
+ synchronized(this) {
+ pinnedApps = mPinnedApps.values();
+ }
+ for (PinnedApp pinnedApp : pinnedApps) {
+ for (PinnedFile pf : pinnedApp.mFiles) {
+ PinnedFileStat stat =
+ new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
+ stats.add(stat);
+ }
+ }
+
+ Collection<PinnedFile> pinnedFiles;
+ synchronized(this) {
+ pinnedFiles = mPinnedFiles.values();
+ }
+ for (PinnedFile pf : pinnedFiles) {
+ PinnedFileStat stat = new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
+ stats.add(stat);
+ }
+ if (mCurrentlyPinnedAnonSize > 0) {
+ stats.add(new PinnedFileStat(ANON_REGION_STAT_NAME,
+ mCurrentlyPinnedAnonSize, ANON_REGION_STAT_NAME));
+ }
+ return stats;
+ }
+
+ public final class BinderService extends IPinnerService.Stub {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ HashSet<PinnedFile> shownPins = new HashSet<>();
+ HashSet<String> groups = new HashSet<>();
+ final int bytesPerMB = 1024 * 1024;
synchronized (PinnerService.this) {
long totalSize = 0;
- for (PinnedFile pinnedFile : mPinnedFiles) {
- pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
- totalSize += pinnedFile.bytesPinned;
- }
- pw.println();
for (int key : mPinnedApps.keySet()) {
PinnedApp app = mPinnedApps.get(key);
pw.print(getNameForKey(key));
@@ -1223,14 +1399,53 @@ public final class PinnerService extends SystemService {
pw.print(" active="); pw.print(app.active);
pw.println();
for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
- pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
+ pw.print(" ");
+ pw.format("%s pinned:%d bytes (%d MB) pinlist:%b\n", pf.fileName,
+ pf.bytesPinned, pf.bytesPinned / bytesPerMB, pf.used_pinlist);
totalSize += pf.bytesPinned;
+ shownPins.add(pf);
+ for (PinnedFile dep : pf.pinnedDeps) {
+ pw.print(" ");
+ pw.format("%s pinned:%d bytes (%d MB) pinlist:%b (Dependency)\n", dep.fileName,
+ dep.bytesPinned, dep.bytesPinned / bytesPerMB, dep.used_pinlist);
+ totalSize += dep.bytesPinned;
+ shownPins.add(dep);
+ }
+ }
+ }
+ pw.println();
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
+ if (!groups.contains(pinnedFile.groupName)) {
+ groups.add(pinnedFile.groupName);
}
}
+ boolean firstPinInGroup = true;
+ for (String group : groups) {
+ List<PinnedFile> groupPins = getAllPinsForGroup(group);
+ for (PinnedFile pinnedFile : groupPins) {
+ if (shownPins.contains(pinnedFile)) {
+ // Already showed in the dump and accounted for, skip.
+ continue;
+ }
+ if (firstPinInGroup) {
+ firstPinInGroup = false;
+ // Ensure we only print when there are pins for groups not yet shown
+ // in the pinned app section.
+ pw.print("Group:" + group);
+ pw.println();
+ }
+ pw.format(" %s pinned:%d bytes (%d MB) pinlist:%b\n", pinnedFile.fileName,
+ pinnedFile.bytesPinned, pinnedFile.bytesPinned / bytesPerMB,
+ pinnedFile.used_pinlist);
+ totalSize += pinnedFile.bytesPinned;
+ }
+ }
+ pw.println();
if (mPinAnonAddress != 0) {
- pw.format("Pinned anon region: %s\n", mCurrentlyPinnedAnonSize);
+ pw.format("Pinned anon region: %d (%d MB)\n", mCurrentlyPinnedAnonSize, mCurrentlyPinnedAnonSize / bytesPerMB);
+ totalSize += mCurrentlyPinnedAnonSize;
}
- pw.format("Total size: %s\n", totalSize);
+ pw.format("Total pinned: %s bytes (%s MB)\n", totalSize, totalSize / bytesPerMB);
pw.println();
if (!mPendingRepin.isEmpty()) {
pw.print("Pending repin: ");
@@ -1277,14 +1492,29 @@ public final class PinnerService extends SystemService {
resultReceiver.send(0, null);
}
+
+ @EnforcePermission(android.Manifest.permission.DUMP)
+ @Override
+ public List<PinnedFileStat> getPinnerStats() {
+ getPinnerStats_enforcePermission();
+ return PinnerService.this.getPinnerStats();
+ }
}
- private static final class PinnedFile implements AutoCloseable {
+ @VisibleForTesting
+ public static final class PinnedFile implements AutoCloseable {
private long mAddress;
final int mapSize;
final String fileName;
final int bytesPinned;
+ // Whether this file was pinned using a pinlist
+ boolean used_pinlist;
+
+ // User defined group name for pinner accounting
+ String groupName = "";
+ ArrayList<PinnedFile> pinnedDeps = new ArrayList<>();
+
PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
mAddress = address;
this.mapSize = mapSize;
@@ -1298,6 +1528,11 @@ public final class PinnerService extends SystemService {
safeMunmap(mAddress, mapSize);
mAddress = -1;
}
+ for (PinnedFile dep : pinnedDeps) {
+ if (dep != null) {
+ dep.close();
+ }
+ }
}
@Override
@@ -1355,5 +1590,4 @@ public final class PinnerService extends SystemService {
}
}
}
-
}
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
new file mode 100644
index 000000000000..98039be20897
--- /dev/null
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ISecurityStateManager;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewUpdateService;
+
+import com.android.internal.R;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SecurityStateManagerService extends ISecurityStateManager.Stub {
+
+ private static final String TAG = "SecurityStateManagerService";
+
+ static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build"
+ + ".security_patch";
+ static final Pattern KERNEL_RELEASE_PATTERN = Pattern.compile("(\\d+\\.\\d+\\.\\d+)("
+ + ".*)");
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public SecurityStateManagerService(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public Bundle getGlobalSecurityState() {
+ Bundle globalSecurityState = new Bundle();
+ globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
+ globalSecurityState.putString(KEY_VENDOR_SPL,
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ String moduleMetadataProviderPackageName =
+ mContext.getString(R.string.config_defaultModuleMetadataProvider);
+ if (!moduleMetadataProviderPackageName.isEmpty()) {
+ globalSecurityState.putString(moduleMetadataProviderPackageName,
+ getSpl(moduleMetadataProviderPackageName));
+ }
+ globalSecurityState.putString(KEY_KERNEL_VERSION, getKernelVersion());
+ addWebViewPackages(globalSecurityState);
+ addSecurityStatePackages(globalSecurityState);
+ return globalSecurityState;
+ }
+
+ private String getSpl(String packageName) {
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ return mPackageManager.getPackageInfo(packageName, 0 /* flags */).versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, TextUtils.formatSimple("Failed to get SPL for package %s.",
+ packageName), e);
+ }
+ }
+ return "";
+ }
+
+ private String getKernelVersion() {
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return "";
+ }
+
+ private void addWebViewPackages(Bundle bundle) {
+ for (WebViewProviderInfo info : WebViewUpdateService.getAllWebViewPackages()) {
+ String packageName = info.packageName;
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+
+ private void addSecurityStatePackages(Bundle bundle) {
+ String[] packageNames;
+ packageNames = mContext.getResources().getStringArray(R.array.config_securityStatePackages);
+ for (String packageName : packageNames) {
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f6835feeea16..39b8643e6d38 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -215,7 +215,7 @@ class StorageManagerService extends IStorageManager.Stub
public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
/** Extended timeout for the system server watchdog. */
- private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000;
+ private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 20 * 1000;
/** Extended timeout for the system server watchdog for vold#partition operation. */
private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
@@ -1235,11 +1235,16 @@ class StorageManagerService extends IStorageManager.Stub
}
}
+ private void extendWatchdogTimeout(String reason) {
+ Watchdog w = Watchdog.getInstance();
+ w.pauseWatchingMonitorsFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ w.pauseWatchingCurrentThreadFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ }
+
private void onUserStopped(int userId) {
Slog.d(TAG, "onUserStopped " + userId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
try {
mVold.onUserStopped(userId);
mStoraged.onUserStopped(userId);
@@ -1322,8 +1327,7 @@ class StorageManagerService extends IStorageManager.Stub
unlockedUsers.add(userId);
}
}
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
for (Integer userId : unlockedUsers) {
try {
mVold.onUserStopped(userId);
@@ -2343,8 +2347,7 @@ class StorageManagerService extends IStorageManager.Stub
try {
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
+ extendWatchdogTimeout("#mount might be slow");
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
public boolean onVolumeChecking(FileDescriptor fd, String path,
@@ -2474,8 +2477,7 @@ class StorageManagerService extends IStorageManager.Stub
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2493,8 +2495,7 @@ class StorageManagerService extends IStorageManager.Stub
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2512,8 +2513,7 @@ class StorageManagerService extends IStorageManager.Stub
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -3622,8 +3622,7 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public ParcelFileDescriptor open() throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
+ extendWatchdogTimeout("#open might be slow");
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
@@ -3636,8 +3635,7 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
+ extendWatchdogTimeout("#openFile might be slow");
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
@@ -3648,8 +3646,7 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void close() throws Exception {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
+ extendWatchdogTimeout("#close might be slow");
if (mMounted) {
BackgroundThread.getHandler().post(() -> {
try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c718d392a610..9eb35fde50fb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2096,14 +2096,48 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
/**
* Send a notification to registrants about the data activity state.
*
+ * @param subId the subscriptionId for the data connection
+ * @param state indicates the latest data activity type
+ * e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
+ *
+ */
+
+ public void notifyDataActivityForSubscriber(int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivity()")) {
+ return;
+ }
+ int phoneId = getPhoneIdFromSubId(subId);
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mDataActivity[phoneId] = state;
+ for (Record r : mRecords) {
+ // Notify by correct subId.
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onDataActivity(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ /**
+ * Send a notification to registrants about the data activity state.
+ *
* @param phoneId the phoneId carrying the data connection
* @param subId the subscriptionId for the data connection
* @param state indicates the latest data activity type
* e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
*
*/
- public void notifyDataActivityForSubscriber(int phoneId, int subId, int state) {
- if (!checkNotifyPermission("notifyDataActivity()" )) {
+ public void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivityWithSlot()")) {
return;
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 003046ab884f..60f087f53a7e 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -69,6 +69,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -98,11 +99,17 @@ public class Watchdog implements Dumpable {
// applications may not work with a debug build. CTS will fail.
private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000;
+ // This ratio is used to compute the pre-watchdog timeout (2 means that the pre-watchdog timeout
+ // will be half the full timeout).
+ //
+ // The pre-watchdog event is similar to a full watchdog except it does not crash system server.
+ private static final int PRE_WATCHDOG_TIMEOUT_RATIO = 2;
+
// These are temporally ordered: larger values as lateness increases
- private static final int COMPLETED = 0;
- private static final int WAITING = 1;
- private static final int WAITED_HALF = 2;
- private static final int OVERDUE = 3;
+ static final int COMPLETED = 0;
+ static final int WAITING = 1;
+ static final int WAITED_UNTIL_PRE_WATCHDOG = 2;
+ static final int OVERDUE = 3;
// Track watchdog timeout history and break the crash loop if there is.
private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt";
@@ -237,10 +244,8 @@ public class Watchdog implements Dumpable {
}
}
- /**
- * Used for checking status of handle threads and scheduling monitor callbacks.
- */
- public final class HandlerChecker implements Runnable {
+ /** Used for checking status of handle threads and scheduling monitor callbacks. */
+ public static class HandlerChecker implements Runnable {
private final Handler mHandler;
private final String mName;
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
@@ -251,11 +256,19 @@ public class Watchdog implements Dumpable {
private long mStartTimeMillis;
private int mPauseCount;
private long mPauseEndTimeMillis;
+ private Clock mClock;
+ private Object mLock;
- HandlerChecker(Handler handler, String name) {
+ HandlerChecker(Handler handler, String name, Object lock, Clock clock) {
mHandler = handler;
mName = name;
+ mLock = lock;
mCompleted = true;
+ mClock = clock;
+ }
+
+ HandlerChecker(Handler handler, String name, Object lock) {
+ this(handler, name, lock, SystemClock.uptimeClock());
}
void addMonitorLocked(Monitor monitor) {
@@ -278,11 +291,9 @@ public class Watchdog implements Dumpable {
mMonitorQueue.clear();
}
- long nowMillis = SystemClock.uptimeMillis();
- boolean isPaused = mPauseCount > 0
- || (mPauseEndTimeMillis > 0 && mPauseEndTimeMillis < nowMillis);
- if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
- || isPaused) {
+ long nowMillis = mClock.millis();
+ boolean isPaused = mPauseCount > 0 || mPauseEndTimeMillis > nowMillis;
+ if ((mMonitors.size() == 0 && isHandlerPolling()) || isPaused) {
// Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
@@ -305,15 +316,19 @@ public class Watchdog implements Dumpable {
mHandler.postAtFrontOfQueue(this);
}
+ boolean isHandlerPolling() {
+ return mHandler.getLooper().getQueue().isPolling();
+ }
+
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
} else {
- long latency = SystemClock.uptimeMillis() - mStartTimeMillis;
- if (latency < mWaitMaxMillis / 2) {
+ long latency = mClock.millis() - mStartTimeMillis;
+ if (latency < mWaitMaxMillis / PRE_WATCHDOG_TIMEOUT_RATIO) {
return WAITING;
} else if (latency < mWaitMaxMillis) {
- return WAITED_HALF;
+ return WAITED_UNTIL_PRE_WATCHDOG;
}
}
return OVERDUE;
@@ -334,7 +349,7 @@ public class Watchdog implements Dumpable {
} else {
prefix = "Blocked in monitor " + mCurrentMonitor.getClass().getName();
}
- long latencySeconds = (SystemClock.uptimeMillis() - mStartTimeMillis) / 1000;
+ long latencySeconds = (mClock.millis() - mStartTimeMillis) / 1000;
return prefix + " on " + mName + " (" + getThread().getName() + ")"
+ " for " + latencySeconds + "s";
}
@@ -366,10 +381,11 @@ public class Watchdog implements Dumpable {
* the given time.
*/
public void pauseForLocked(int pauseMillis, String reason) {
- mPauseEndTimeMillis = SystemClock.uptimeMillis() + pauseMillis;
+ mPauseEndTimeMillis = mClock.millis() + pauseMillis;
// Mark as completed, because there's a chance we called this after the watchog
- // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
- // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+ // want to ensure the next call to #getCompletionStateLocked for this checker returns
+ // 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing of HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause end time: " + mPauseEndTimeMillis);
@@ -379,8 +395,9 @@ public class Watchdog implements Dumpable {
public void pauseLocked(String reason) {
mPauseCount++;
// Mark as completed, because there's a chance we called this after the watchog
- // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
- // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+ // want to ensure the next call to #getCompletionStateLocked for this checker returns
+ // 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause count: " + mPauseCount);
@@ -396,6 +413,11 @@ public class Watchdog implements Dumpable {
Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
}
}
+
+ @Override
+ public String toString() {
+ return "CheckerHandler for " + mName;
+ }
}
final class RebootRequestReceiver extends BroadcastReceiver {
@@ -445,31 +467,40 @@ public class Watchdog implements Dumpable {
ServiceThread t = new ServiceThread("watchdog.monitor",
android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
t.start();
- mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread");
+ mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread", mLock);
mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(FgThread.getHandler(), "foreground thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(FgThread.getHandler(), "foreground thread", mLock)));
// Add checker for main thread. We only do a quick check since there
// can be UI running on the thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ new Handler(Looper.getMainLooper()), "main thread", mLock)));
// Add checker for shared UI thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(UiThread.getHandler(), "ui thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(new HandlerChecker(UiThread.getHandler(), "ui thread", mLock)));
// And also check IO thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(IoThread.getHandler(), "i/o thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(new HandlerChecker(IoThread.getHandler(), "i/o thread", mLock)));
// And the display thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(DisplayThread.getHandler(), "display thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(DisplayThread.getHandler(), "display thread", mLock)));
// And the animation thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(AnimationThread.getHandler(), "animation thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ AnimationThread.getHandler(), "animation thread", mLock)));
// And the surface animation thread.
- mHandlerCheckers.add(withDefaultTimeout(
- new HandlerChecker(SurfaceAnimationThread.getHandler(),
- "surface animation thread")));
+ mHandlerCheckers.add(
+ withDefaultTimeout(
+ new HandlerChecker(
+ SurfaceAnimationThread.getHandler(),
+ "surface animation thread",
+ mLock)));
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
@@ -609,7 +640,7 @@ public class Watchdog implements Dumpable {
public void addThread(Handler thread) {
synchronized (mLock) {
final String name = thread.getLooper().getThread().getName();
- mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name)));
+ mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name, mLock)));
}
}
@@ -617,7 +648,7 @@ public class Watchdog implements Dumpable {
synchronized (mLock) {
final String name = thread.getLooper().getThread().getName();
mHandlerCheckers.add(
- withCustomTimeout(new HandlerChecker(thread, name), timeoutMillis));
+ withCustomTimeout(new HandlerChecker(thread, name, mLock), timeoutMillis));
}
}
@@ -797,11 +828,11 @@ public class Watchdog implements Dumpable {
String subject = "";
boolean allowRestart = true;
int debuggerWasConnected = 0;
- boolean doWaitedHalfDump = false;
+ boolean doWaitedPreDump = false;
// The value of mWatchdogTimeoutMillis might change while we are executing the loop.
// We store the current value to use a consistent value for all handlers.
final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
- final long checkIntervalMillis = watchdogTimeoutMillis / 2;
+ final long checkIntervalMillis = watchdogTimeoutMillis / PRE_WATCHDOG_TIMEOUT_RATIO;
final ArrayList<Integer> pids;
synchronized (mLock) {
long timeout = checkIntervalMillis;
@@ -848,15 +879,16 @@ public class Watchdog implements Dumpable {
} else if (waitState == WAITING) {
// still waiting but within their configured intervals; back off and recheck
continue;
- } else if (waitState == WAITED_HALF) {
+ } else if (waitState == WAITED_UNTIL_PRE_WATCHDOG) {
if (!waitedHalf) {
- Slog.i(TAG, "WAITED_HALF");
+ Slog.i(TAG, "WAITED_UNTIL_PRE_WATCHDOG");
waitedHalf = true;
- // We've waited half, but we'd need to do the stack trace dump w/o the lock.
- blockedCheckers = getCheckersWithStateLocked(WAITED_HALF);
+ // We've waited until the pre-watchdog, but we'd need to do the stack trace
+ // dump w/o the lock.
+ blockedCheckers = getCheckersWithStateLocked(WAITED_UNTIL_PRE_WATCHDOG);
subject = describeCheckersLocked(blockedCheckers);
pids = new ArrayList<>(mInterestingJavaPids);
- doWaitedHalfDump = true;
+ doWaitedPreDump = true;
} else {
continue;
}
@@ -874,12 +906,12 @@ public class Watchdog implements Dumpable {
// First collect stack traces from all threads of the system process.
//
// Then, if we reached the full timeout, kill this process so that the system will
- // restart. If we reached half of the timeout, just log some information and continue.
- logWatchog(doWaitedHalfDump, subject, pids);
+ // restart. If we reached pre-watchdog timeout, just log some information and continue.
+ logWatchog(doWaitedPreDump, subject, pids);
- if (doWaitedHalfDump) {
- // We have waited for only half of the timeout, we continue to wait for the duration
- // of the full timeout before killing the process.
+ if (doWaitedPreDump) {
+ // We have waited for only pre-watchdog timeout, we continue to wait for the
+ // duration of the full timeout before killing the process.
continue;
}
@@ -928,8 +960,8 @@ public class Watchdog implements Dumpable {
}
}
- private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) {
- // Get critical event log before logging the half watchdog so that it doesn't
+ private void logWatchog(boolean preWatchdog, String subject, ArrayList<Integer> pids) {
+ // Get critical event log before logging the pre-watchdog so that it doesn't
// occur in the log.
String criticalEvents =
CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
@@ -941,7 +973,7 @@ public class Watchdog implements Dumpable {
}
final String dropboxTag;
- if (halfWatchdog) {
+ if (preWatchdog) {
dropboxTag = "pre_watchdog";
CriticalEventLog.getInstance().logHalfWatchdog(subject);
FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED);
@@ -971,7 +1003,7 @@ public class Watchdog implements Dumpable {
report.append(processCpuTracker.printCurrentState(anrTime, 10));
report.append(tracesFileException.getBuffer());
- if (!halfWatchdog) {
+ if (!preWatchdog) {
// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the
// kernel log
doSysRq('w');
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54c8ed38bb1c..b87d02d86c22 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,7 +79,6 @@ import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_
import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
-import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
@@ -5051,7 +5050,7 @@ public class ActivityManagerService extends IActivityManager.Stub
REASON_LOCKED_BOOT_COMPLETED);
}
// Send BOOT_COMPLETED if the user is unlocked
- if (StorageManager.isUserKeyUnlocked(app.userId)) {
+ if (StorageManager.isCeStorageUnlocked(app.userId)) {
sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_BOOT_COMPLETED),
REASON_BOOT_COMPLETED);
}
@@ -14150,7 +14149,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|| action.startsWith("android.intent.action.PACKAGE_")
|| action.startsWith("android.intent.action.UID_")
|| action.startsWith("android.intent.action.EXTERNAL_")
- || action.startsWith("android.bluetooth.")) {
+ || action.startsWith("android.bluetooth.")
+ || action.equals(Intent.ACTION_SHUTDOWN)) {
if (DEBUG_BROADCAST) {
Slog.wtf(TAG,
"System internals registering for " + filter.toLongString()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 36356bd95128..1928780fa090 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -120,12 +120,15 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.AggregatedPowerStatsConfig;
import com.android.server.power.stats.BatteryExternalStatsWorker;
+import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
+import com.android.server.power.stats.PowerStatsExporter;
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
@@ -181,6 +184,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private final AtomicFile mConfigFile;
+ private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
+ private final PowerStatsUidResolver mPowerStatsUidResolver;
private volatile boolean mMonitorEnabled = true;
@@ -408,9 +413,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
.setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
.build();
+ mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
systemDir, handler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, mPowerStatsUidResolver);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
@@ -419,8 +425,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
AggregatedPowerStatsConfig aggregatedPowerStatsConfig = getAggregatedPowerStatsConfig();
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig);
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
- mPowerStatsStore);
mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig,
mStats.getHistory());
final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
@@ -429,7 +433,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
com.android.internal.R.integer.config_powerStatsAggregationPeriod);
mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore,
- Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats, mBatteryUsageStatsProvider);
+ Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats);
+ PowerStatsExporter powerStatsExporter =
+ new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
+ powerStatsExporter, mPowerProfile, mCpuScalingPolicies,
+ mPowerStatsStore, Clock.SYSTEM_CLOCK);
+ mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore);
+ mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
}
@@ -469,9 +480,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
public void systemServicesReady() {
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
+ mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
- mWorker.systemServicesReady();
final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
@@ -775,25 +787,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
void addIsolatedUid(final int isolatedUid, final int appUid) {
- synchronized (mLock) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
- mHandler.post(() -> {
- synchronized (mStats) {
- mStats.addIsolatedUidLocked(isolatedUid, appUid, elapsedRealtime, uptime);
- }
- });
- }
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, appUid);
+ FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
void removeIsolatedUid(final int isolatedUid, final int appUid) {
- synchronized (mLock) {
- mHandler.post(() -> {
- synchronized (mStats) {
- mStats.scheduleRemoveIsolatedUidLocked(isolatedUid, appUid);
- }
- });
- }
+ mPowerStatsUidResolver.noteIsolatedUidRemoved(isolatedUid, appUid);
+ FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, isolatedUid,
+ FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
}
void noteProcessStart(final String name, final int uid) {
@@ -877,12 +879,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub
awaitCompletion();
- if (mBatteryUsageStatsProvider.shouldUpdateStats(queries,
+ if (BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ SystemClock.elapsedRealtime(),
mWorker.getLastCollectionTimeStamp())) {
syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
}
- return mBatteryUsageStatsProvider.getBatteryUsageStats(queries);
+ synchronized (mStats) {
+ return mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, queries);
+ }
}
/** Register callbacks for statsd pulled atoms. */
@@ -2723,7 +2728,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
synchronized (mStats) {
mStats.prepareForDumpLocked();
BatteryUsageStats batteryUsageStats =
- mBatteryUsageStatsProvider.getBatteryUsageStats(query);
+ mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, query);
if (proto) {
batteryUsageStats.dumpToProto(fd);
} else {
@@ -3008,11 +3013,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
- checkinStats.dumpProtoLocked(
- mContext, fd, apps, flags, historyStart);
+ checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
+ historyStart, mDumpHelper);
mStats.mCheckinFile.delete();
return;
}
@@ -3026,7 +3031,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
awaitCompletion();
synchronized (mStats) {
- mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+ mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
@@ -3050,11 +3055,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckin(mContext, pw, apps, flags,
- historyStart);
+ historyStart, mDumpHelper);
mStats.mCheckinFile.delete();
return;
}
@@ -3067,7 +3072,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
if (DBG) Slog.d(TAG, "begin dumpCheckin from UID " + Binder.getCallingUid());
awaitCompletion();
- mStats.dumpCheckin(mContext, pw, apps, flags, historyStart);
+ mStats.dumpCheckin(mContext, pw, apps, flags, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
@@ -3076,7 +3081,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
if (DBG) Slog.d(TAG, "begin dump from UID " + Binder.getCallingUid());
awaitCompletion();
- mStats.dump(mContext, pw, flags, reqUid, historyStart);
+ mStats.dump(mContext, pw, flags, reqUid, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cb2b5fbe7a5a..4ff34b1d7faa 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -34,7 +34,7 @@ import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.EAGAIN;
import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -133,7 +133,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
@@ -3299,8 +3298,6 @@ public final class ProcessList {
// about the process state of the isolated UID *before* it is registered with the
// owning application.
mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
- FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
- FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
sdkSandboxClientAppPackage,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 192fd6f1a9ce..f47482d3a70f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -142,6 +142,7 @@ public class SettingsToPropertiesMapper {
"core_experiments_team_internal",
"core_graphics",
"dck_framework",
+ "devoptions_settings",
"game",
"haptics",
"hardware_backed_security_mainline",
@@ -152,11 +153,13 @@ public class SettingsToPropertiesMapper {
"mainline_sdk",
"media_audio",
"media_drm",
+ "media_reliability",
"media_tv",
"media_solutions",
"nfc",
"pdf_viewer",
"pixel_audio_android",
+ "pixel_bluetooth",
"pixel_system_sw_touch",
"pixel_watch",
"platform_security",
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
index 0ee7d9cdd3d4..091696737dcf 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
@@ -20,6 +20,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -150,7 +151,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
synchronized (mLock) {
SparseIntArray opModes = mUidModes.get(uid, null);
if (opModes == null) {
@@ -176,7 +177,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
synchronized (mLock) {
SparseIntArray opModes = mUidModes.get(uid, null);
if (opModes == null) {
@@ -187,7 +188,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
final int defaultMode = AppOpsManager.opToDefaultMode(op);
List<AppOpsModeChangedListener> listenersCopy;
synchronized (mLock) {
@@ -329,7 +330,7 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
SparseBooleanArray result = new SparseBooleanArray();
synchronized (mLock) {
SparseIntArray modes = mUidModes.get(uid);
@@ -606,9 +607,17 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
for (final String pkg : packagesDeclaringPermission) {
for (int userId : userIds) {
final int uid = pmi.getPackageUid(pkg, 0, userId);
- final int oldMode = getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ final int oldMode =
+ getUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_SCHEDULE_EXACT_ALARM);
if (oldMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
- setUidMode(uid, OP_SCHEDULE_EXACT_ALARM, MODE_ALLOWED);
+ setUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_SCHEDULE_EXACT_ALARM,
+ MODE_ALLOWED);
}
}
// This appop is meant to be controlled at a uid level. So we leave package modes as
@@ -641,7 +650,10 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
final int flags = permissionManager.getPermissionFlags(pkg, permissionName,
UserHandle.of(userId));
if ((flags & PackageManager.FLAG_PERMISSION_USER_SET) == 0) {
- setUidMode(uid, OP_USE_FULL_SCREEN_INTENT,
+ setUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_USE_FULL_SCREEN_INTENT,
AppOpsManager.opToDefaultMode(OP_USE_FULL_SCREEN_INTENT));
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
index f6e6bc0be8fa..f056f6b10b0d 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
@@ -59,8 +59,9 @@ public interface AppOpsCheckingServiceInterface {
* Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
* Returns an empty SparseIntArray if nothing is set.
* @param uid for which we need the app-ops and their modes.
+ * @param persistentDeviceId device for which we need the app-ops and their modes
*/
- SparseIntArray getNonDefaultUidModes(int uid);
+ SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId);
/**
* Returns a copy of non-default app-ops with op as keys and their modes as values for a package
@@ -75,20 +76,22 @@ public interface AppOpsCheckingServiceInterface {
* Returns the app-op mode for a particular app-op of a uid.
* Returns default op mode if the op mode for particular uid and op is not set.
* @param uid user id for which we need the mode.
+ * @param persistentDeviceId device for which we need the mode
* @param op app-op for which we need the mode.
* @return mode of the app-op.
*/
- int getUidMode(int uid, int op);
+ int getUidMode(int uid, String persistentDeviceId, int op);
/**
* Set the app-op mode for a particular uid and op.
* The mode is not set if the mode is the same as the default mode for the op.
* @param uid user id for which we want to set the mode.
+ * @param persistentDeviceId device for which we want to set the mode.
* @param op app-op for which we want to set the mode.
* @param mode mode for the app-op.
* @return true if op mode is changed.
*/
- boolean setUidMode(int uid, int op, @Mode int mode);
+ boolean setUidMode(int uid, String persistentDeviceId, int op, @Mode int mode);
/**
* Gets the app-op mode for a particular package.
@@ -130,10 +133,11 @@ public interface AppOpsCheckingServiceInterface {
/**
* @param uid UID to query foreground ops for.
+ * @param persistentDeviceId device to query foreground ops for
* @return SparseBooleanArray where the keys are the op codes for which their modes are
* MODE_FOREGROUND for the passed UID.
*/
- SparseBooleanArray getForegroundOps(int uid);
+ SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId);
/**
*
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
index ccdf3a5baa7b..f6da166d9a34 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
@@ -60,9 +60,9 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
Log.i(LOG_TAG, "getNonDefaultUidModes(uid = " + uid + ")");
- return mService.getNonDefaultUidModes(uid);
+ return mService.getNonDefaultUidModes(uid, persistentDeviceId);
}
@Override
@@ -73,15 +73,15 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
Log.i(LOG_TAG, "getUidMode(uid = " + uid + ", op = " + op + ")");
- return mService.getUidMode(uid, op);
+ return mService.getUidMode(uid, persistentDeviceId, op);
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
Log.i(LOG_TAG, "setUidMode(uid = " + uid + ", op = " + op + ", mode = " + mode + ")");
- return mService.setUidMode(uid, op, mode);
+ return mService.setUidMode(uid, persistentDeviceId, op, mode);
}
@Override
@@ -117,9 +117,9 @@ public class AppOpsCheckingServiceLoggingDecorator implements AppOpsCheckingServ
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
Log.i(LOG_TAG, "getForegroundOps(uid = " + uid + ")");
- return mService.getForegroundOps(uid);
+ return mService.getForegroundOps(uid, persistentDeviceId);
}
@Override
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index c3a02a84a277..55cf7ed80483 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -81,11 +81,11 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getNonDefaultUidModes");
try {
- return mService.getNonDefaultUidModes(uid);
+ return mService.getNonDefaultUidModes(uid, persistentDeviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -103,20 +103,21 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getUidMode");
try {
- return mService.getUidMode(uid, op);
+ return mService.getUidMode(uid, persistentDeviceId, op);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean setUidMode(int uid, int op, @AppOpsManager.Mode int mode) {
+ public boolean setUidMode(
+ int uid, String persistentDeviceId, int op, @AppOpsManager.Mode int mode) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#setUidMode");
try {
- return mService.setUidMode(uid, op, mode);
+ return mService.setUidMode(uid, persistentDeviceId, op, mode);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -179,11 +180,11 @@ public class AppOpsCheckingServiceTracingDecorator implements AppOpsCheckingServ
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getForegroundOps");
try {
- return mService.getForegroundOps(uid);
+ return mService.getForegroundOps(uid, persistentDeviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 14aab13bf638..344673793700 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -63,6 +63,7 @@ import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
import static android.app.AppOpsManager.opRestrictsRead;
import static android.app.AppOpsManager.opToName;
import static android.app.AppOpsManager.opToPublicName;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
@@ -1349,7 +1350,10 @@ public class AppOpsService extends IAppOpsService.Stub {
SparseBooleanArray foregroundOps = new SparseBooleanArray();
- SparseBooleanArray uidForegroundOps = mAppOpsCheckingService.getForegroundOps(uid);
+ // TODO(b/299330771): Check uidForegroundOps for all devices.
+ SparseBooleanArray uidForegroundOps =
+ mAppOpsCheckingService.getForegroundOps(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT);
for (int i = 0; i < uidForegroundOps.size(); i++) {
foregroundOps.put(uidForegroundOps.keyAt(i), true);
}
@@ -1369,10 +1373,16 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
final int code = foregroundOps.keyAt(fgi);
-
- if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ // TODO(b/299330771): Notify op changes for all relevant devices.
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code)
!= AppOpsManager.opToDefaultMode(code)
- && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ && mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code)
== AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
@@ -1489,7 +1499,11 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- final SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ // TODO(b/299330771): Make this methods device-aware, currently it represents only the
+ // primary device.
+ final SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
if (opModes == null) {
return null;
}
@@ -1844,16 +1858,22 @@ public class AppOpsService extends IAppOpsService.Stub {
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
}
- if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ // TODO(b/266164193): Ensure this behavior is device-aware after uid op mode for runtime
+ // permissions is deprecated.
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code)
!= AppOpsManager.opToDefaultMode(code)) {
- previousMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
+ previousMode =
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code);
} else {
// doesn't look right but is legacy behavior.
previousMode = MODE_DEFAULT;
}
mIgnoredCallback = permissionPolicyCallback;
- if (!mAppOpsCheckingService.setUidMode(uidState.uid, code, mode)) {
+ if (!mAppOpsCheckingService.setUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code, mode)) {
return;
}
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2275,8 +2295,10 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean changed = false;
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
-
- SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ // TODO(b/299330771): Check non default modes for all devices.
+ SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
@@ -2285,7 +2307,12 @@ public class AppOpsService extends IAppOpsService.Stub {
int previousMode = opModes.valueAt(j);
int newMode = isUidOpGrantedByRole(uidState.uid, code) ? MODE_ALLOWED :
AppOpsManager.opToDefaultMode(code);
- mAppOpsCheckingService.setUidMode(uidState.uid, code, newMode);
+ // TODO(b/299330771): Set mode for all necessary devices.
+ mAppOpsCheckingService.setUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code,
+ newMode);
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -2601,10 +2628,14 @@ public class AppOpsService extends IAppOpsService.Stub {
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
+ // TODO(b/299330771): Check mode for the relevant device.
if (uidState != null
- && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ && mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code)
!= AppOpsManager.opToDefaultMode(code)) {
- final int rawMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
+ final int rawMode =
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
@@ -2851,13 +2882,19 @@ public class AppOpsService extends IAppOpsService.Stub {
return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
packageName);
}
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -3396,13 +3433,19 @@ public class AppOpsService extends IAppOpsService.Stub {
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
false);
final int switchCode = AppOpsManager.opToSwitch(code);
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3511,13 +3554,19 @@ public class AppOpsService extends IAppOpsService.Stub {
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
false);
final int switchCode = AppOpsManager.opToSwitch(code);
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default mode per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -5664,8 +5713,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
+ // TODO(b/299330771): Get modes for all devices.
final SparseIntArray opModes =
- mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
index 98e6476e9707..c9fa9e600ecc 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
@@ -65,9 +65,9 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
- SparseIntArray oldVal = mOldImplementation.getNonDefaultUidModes(uid);
- SparseIntArray newVal = mNewImplementation.getNonDefaultUidModes(uid);
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
+ SparseIntArray oldVal = mOldImplementation.getNonDefaultUidModes(uid, persistentDeviceId);
+ SparseIntArray newVal = mNewImplementation.getNonDefaultUidModes(uid, persistentDeviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getNonDefaultUidModes");
@@ -89,9 +89,9 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
- public int getUidMode(int uid, int op) {
- int oldVal = mOldImplementation.getUidMode(uid, op);
- int newVal = mNewImplementation.getUidMode(uid, op);
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
+ int oldVal = mOldImplementation.getUidMode(uid, persistentDeviceId, op);
+ int newVal = mNewImplementation.getUidMode(uid, persistentDeviceId, op);
if (oldVal != newVal) {
signalImplDifference("getUidMode");
@@ -101,9 +101,9 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
- boolean oldVal = mOldImplementation.setUidMode(uid, op, mode);
- boolean newVal = mNewImplementation.setUidMode(uid, op, mode);
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
+ boolean oldVal = mOldImplementation.setUidMode(uid, persistentDeviceId, op, mode);
+ boolean newVal = mNewImplementation.setUidMode(uid, persistentDeviceId, op, mode);
if (oldVal != newVal) {
signalImplDifference("setUidMode");
@@ -155,9 +155,9 @@ public class AppOpsServiceTestingShim implements AppOpsCheckingServiceInterface
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
- SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid);
- SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid);
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
+ SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid, persistentDeviceId);
+ SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid, persistentDeviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getForegroundOps");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d4b72c1770f7..5d47e35a8729 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4600,7 +4600,7 @@ public class AudioService extends IAudioService.Stub
}
private void setStreamVolume(int streamType, int index, int flags,
- @NonNull AudioDeviceAttributes ada,
+ @Nullable AudioDeviceAttributes ada,
String callingPackage, String caller, String attributionTag, int uid,
boolean hasModifyAudioSettings,
boolean canChangeMuteAndUpdateController) {
@@ -4618,7 +4618,9 @@ public class AudioService extends IAudioService.Stub
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
- final int device = ada.getInternalType();
+ final int device = (ada == null)
+ ? getDeviceForStream(streamType)
+ : ada.getInternalType();
int oldIndex;
// skip a2dp absolute volume control request when the device
@@ -10665,8 +10667,8 @@ public class AudioService extends IAudioService.Stub
/** @see LoudnessCodecConfigurator#addMediaCodec(MediaCodec) */
@Override
- public void addLoudnessCodecInfo(int piid, LoudnessCodecInfo codecInfo) {
- mLoudnessCodecHelper.addLoudnessCodecInfo(piid, codecInfo);
+ public void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo codecInfo) {
+ mLoudnessCodecHelper.addLoudnessCodecInfo(piid, mediaCodecHash, codecInfo);
}
/** @see LoudnessCodecConfigurator#removeMediaCodec(MediaCodec) */
@@ -11370,6 +11372,8 @@ public class AudioService extends IAudioService.Stub
static final int LOG_NB_EVENTS_SPATIAL = 30;
static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
+ static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
+
static final EventLogger
sLifecycleLogger = new EventLogger(LOG_NB_EVENTS_LIFECYCLE,
"audio services lifecycle");
@@ -11569,6 +11573,10 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.dump(pw);
sSpatialLogger.dump(pw);
+ pw.println("\n");
+ pw.println("\nLoudness alignment:");
+ mLoudnessCodecHelper.dump(pw);
+
mAudioSystem.dump(pw);
}
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index f69b9f6523cc..de8901179028 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -622,6 +622,55 @@ public class AudioServiceEvents {
}
}
+ static final class LoudnessEvent extends EventLogger.Event {
+ static final int START_PIID = 0;
+
+ static final int STOP_PIID = 1;
+
+ static final int CLIENT_DIED = 2;
+
+ final int mEventType;
+ final int mIntValue1;
+ final int mIntValue2;
+
+ private LoudnessEvent(int event, int i1, int i2) {
+ mEventType = event;
+ mIntValue1 = i1;
+ mIntValue2 = i2;
+ }
+
+ static LoudnessEvent getStartPiid(int piid, int pid) {
+ return new LoudnessEvent(START_PIID, piid, pid);
+ }
+
+ static LoudnessEvent getStopPiid(int piid, int pid) {
+ return new LoudnessEvent(STOP_PIID, piid, pid);
+ }
+
+ static LoudnessEvent getClientDied(int pid) {
+ return new LoudnessEvent(CLIENT_DIED, 0 /* ignored */, pid);
+ }
+
+
+ @Override
+ public String eventToString() {
+ switch (mEventType) {
+ case START_PIID:
+ return String.format(
+ "Start loudness updates for piid %d for client pid %d",
+ mIntValue1, mIntValue2);
+ case STOP_PIID:
+ return String.format(
+ "Stop loudness updates for piid %d for client pid %d",
+ mIntValue1, mIntValue2);
+ case CLIENT_DIED:
+ return String.format("Loudness client with pid %d died", mIntValue2);
+
+ }
+ return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
+ }
+ }
+
/**
* Class to log stream type mute/unmute events
*/
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 3c67e9dd116b..bbe819f22e3a 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -21,6 +21,11 @@ import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
import static android.media.AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -41,7 +46,11 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.audio.AudioServiceEvents.LoudnessEvent;
+import com.android.server.utils.EventLogger;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -50,6 +59,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Class to handle the updates in loudness parameters and responsible to generate parameters that
@@ -70,10 +80,14 @@ public class LoudnessCodecHelper {
private static final String SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE =
"audio.loudness.builtin-speaker-spl-range-size";
- private static final int SPL_RANGE_UNKNOWN = 0;
- private static final int SPL_RANGE_SMALL = 1;
- private static final int SPL_RANGE_MEDIUM = 2;
- private static final int SPL_RANGE_LARGE = 3;
+ @VisibleForTesting
+ static final int SPL_RANGE_UNKNOWN = 0;
+ @VisibleForTesting
+ static final int SPL_RANGE_SMALL = 1;
+ @VisibleForTesting
+ static final int SPL_RANGE_MEDIUM = 2;
+ @VisibleForTesting
+ static final int SPL_RANGE_LARGE = 3;
/** The possible transducer SPL ranges as defined in CTA2075 */
@IntDef({
@@ -99,12 +113,19 @@ public class LoudnessCodecHelper {
pid = (Integer) cookie;
}
if (pid != null) {
- mLoudnessCodecHelper.removePid(pid);
+ if (DEBUG) {
+ Log.d(TAG, "Client with pid " + pid + " died, removing from receiving updates");
+ }
+ sLogger.enqueue(LoudnessEvent.getClientDied(pid));
+ mLoudnessCodecHelper.onClientPidDied(pid);
}
super.onCallbackDied(callback, cookie);
}
}
+ private static final EventLogger sLogger = new EventLogger(
+ AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates");
+
private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers =
new LoudnessRemoteCallbackList(this);
@@ -125,7 +146,8 @@ public class LoudnessCodecHelper {
private final AudioService mAudioService;
/** Contains the properties necessary to compute the codec loudness related parameters. */
- private static final class LoudnessCodecInputProperties {
+ @VisibleForTesting
+ static final class LoudnessCodecInputProperties {
private final int mMetadataType;
private final boolean mIsDownmixing;
@@ -200,10 +222,53 @@ public class LoudnessCodecHelper {
}
PersistableBundle createLoudnessParameters() {
- // TODO: create bundle with new parameters
- return new PersistableBundle();
- }
+ PersistableBundle loudnessParams = new PersistableBundle();
+
+ switch (mDeviceSplRange) {
+ case SPL_RANGE_LARGE:
+ // corresponds to -31dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 124);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_MEDIUM:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_SMALL:
+ // corresponds to -16dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 64);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 1);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // limited playback range compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 3);
+ }
+ break;
+ default:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ }
+ return loudnessParams;
+ }
}
@GuardedBy("mLock")
@@ -227,22 +292,25 @@ public class LoudnessCodecHelper {
if (DEBUG) {
Log.d(TAG, "startLoudnessCodecUpdates: piid " + piid + " codecInfos " + codecInfoList);
}
- Set<LoudnessCodecInfo> infoSet;
+
synchronized (mLock) {
if (mStartedPiids.contains(piid)) {
Log.w(TAG, "Already started loudness updates for piid " + piid);
return;
}
- infoSet = new HashSet<>(codecInfoList);
+ Set<LoudnessCodecInfo> infoSet = new HashSet<>(codecInfoList);
mStartedPiids.put(piid, infoSet);
- mPiidToPidCache.put(piid, Binder.getCallingPid());
+ int pid = Binder.getCallingPid();
+ mPiidToPidCache.put(piid, pid);
+
+ sLogger.enqueue(LoudnessEvent.getStartPiid(piid, pid));
}
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
mAudioService.getActivePlaybackConfigurations().stream().filter(
conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
- apc -> updateCodecParametersForConfiguration(apc, infoSet));
+ this::updateCodecParametersForConfiguration);
}
}
@@ -250,20 +318,24 @@ public class LoudnessCodecHelper {
if (DEBUG) {
Log.d(TAG, "stopLoudnessCodecUpdates: piid " + piid);
}
+
synchronized (mLock) {
if (!mStartedPiids.contains(piid)) {
Log.w(TAG, "Loudness updates are already stopped for piid " + piid);
return;
}
mStartedPiids.remove(piid);
+
+ sLogger.enqueue(LoudnessEvent.getStopPiid(piid, mPiidToPidCache.get(piid, -1)));
mPiidToDeviceIdCache.delete(piid);
mPiidToPidCache.delete(piid);
}
}
- void addLoudnessCodecInfo(int piid, LoudnessCodecInfo info) {
+ void addLoudnessCodecInfo(int piid, int mediaCodecHash, LoudnessCodecInfo info) {
if (DEBUG) {
- Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " info " + info);
+ Log.d(TAG, "addLoudnessCodecInfo: piid " + piid + " mcHash " + mediaCodecHash + " info "
+ + info);
}
Set<LoudnessCodecInfo> infoSet;
@@ -280,7 +352,20 @@ public class LoudnessCodecHelper {
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
mAudioService.getActivePlaybackConfigurations().stream().filter(
conf -> conf.getPlayerInterfaceId() == piid).findFirst().ifPresent(
- apc -> updateCodecParametersForConfiguration(apc, Set.of(info)));
+ apc -> {
+ final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
+ if (deviceInfo != null) {
+ PersistableBundle updateBundle = new PersistableBundle();
+ synchronized (mLock) {
+ updateBundle.putPersistableBundle(
+ Integer.toString(mediaCodecHash),
+ getCodecBundle_l(deviceInfo, info));
+ }
+ if (!updateBundle.isDefinitelyEmpty()) {
+ dispatchNewLoudnessParameters(piid, updateBundle);
+ }
+ }
+ });
}
}
@@ -298,24 +383,6 @@ public class LoudnessCodecHelper {
}
}
- void removePid(int pid) {
- if (DEBUG) {
- Log.d(TAG, "Removing pid " + pid + " from receiving updates");
- }
- synchronized (mLock) {
- for (int i = 0; i < mPiidToPidCache.size(); ++i) {
- int piid = mPiidToPidCache.keyAt(i);
- if (mPiidToPidCache.get(piid) == pid) {
- if (DEBUG) {
- Log.d(TAG, "Removing piid " + piid);
- }
- mStartedPiids.delete(piid);
- mPiidToDeviceIdCache.delete(piid);
- }
- }
- }
- }
-
PersistableBundle getLoudnessParams(int piid, LoudnessCodecInfo codecInfo) {
if (DEBUG) {
Log.d(TAG, "getLoudnessParams: piid " + piid + " codecInfo " + codecInfo);
@@ -381,48 +448,77 @@ public class LoudnessCodecHelper {
}
}
- updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc, null));
+ updateApcList.forEach(apc -> updateCodecParametersForConfiguration(apc));
+ }
+
+ /** Updates and dispatches the new loudness parameters for all its registered codecs. */
+ void dump(PrintWriter pw) {
+ // Registered clients
+ pw.println("\nRegistered clients:\n");
+ synchronized (mLock) {
+ for (int i = 0; i < mStartedPiids.size(); ++i) {
+ int piid = mStartedPiids.keyAt(i);
+ int pid = mPiidToPidCache.get(piid, -1);
+ final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
+ pw.println(String.format("Player piid %d pid %d active codec types %s\n", piid,
+ pid, codecInfos.stream().map(Object::toString).collect(
+ Collectors.joining(", "))));
+ }
+ pw.println();
+ }
+
+ sLogger.dump(pw);
+ pw.println();
}
- /** Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
+ private void onClientPidDied(int pid) {
+ synchronized (mLock) {
+ for (int i = 0; i < mPiidToPidCache.size(); ++i) {
+ int piid = mPiidToPidCache.keyAt(i);
+ if (mPiidToPidCache.get(piid) == pid) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing piid " + piid);
+ }
+ mStartedPiids.delete(piid);
+ mPiidToDeviceIdCache.delete(piid);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates and dispatches the new loudness parameters for the {@code codecInfos} set.
*
* @param apc the player configuration for which the loudness parameters are updated.
- * @param codecInfos the codec info for which the parameters are updated. If {@code null},
- * send updates for all the started codecs assigned to {@code apc}
*/
- private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc,
- Set<LoudnessCodecInfo> codecInfos) {
+ private void updateCodecParametersForConfiguration(AudioPlaybackConfiguration apc) {
if (DEBUG) {
- Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc + " codecInfos: "
- + codecInfos);
+ Log.d(TAG, "updateCodecParametersForConfiguration apc:" + apc);
}
+
final PersistableBundle allBundles = new PersistableBundle();
final int piid = apc.getPlayerInterfaceId();
+
synchronized (mLock) {
- if (codecInfos == null) {
- codecInfos = mStartedPiids.get(piid);
- }
+ final Set<LoudnessCodecInfo> codecInfos = mStartedPiids.get(piid);
final AudioDeviceInfo deviceInfo = apc.getAudioDeviceInfo();
if (codecInfos != null && deviceInfo != null) {
for (LoudnessCodecInfo info : codecInfos) {
- allBundles.putPersistableBundle(Integer.toString(info.mediaCodecHashCode),
+ allBundles.putPersistableBundle(Integer.toString(info.hashCode()),
getCodecBundle_l(deviceInfo, info));
}
}
}
if (!allBundles.isDefinitelyEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Dispatching for piid: " + piid + " bundle: " + allBundles);
- }
dispatchNewLoudnessParameters(piid, allBundles);
}
}
private void dispatchNewLoudnessParameters(int piid, PersistableBundle bundle) {
if (DEBUG) {
- Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid);
+ Log.d(TAG, "dispatchNewLoudnessParameters: piid " + piid + " bundle: " + bundle);
}
final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; ++i) {
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 be78ea20d09d..cecde55ef89f 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -149,6 +149,14 @@ public abstract class VirtualDeviceManagerInternal {
public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
/**
+ * Returns the ID of the device which owns the display with the given ID.
+ *
+ * <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then
+ * {@link android.content.Context#DEVICE_ID_DEFAULT} is returned.</p>
+ */
+ public abstract int getDeviceIdForDisplayId(int displayId);
+
+ /**
* Gets the persistent ID for the VirtualDevice with the given device ID.
*
* @param deviceId which device we're asking about
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index debf828abf0a..d848f4b6cce5 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -121,6 +121,7 @@ public abstract class BrightnessMappingStrategy {
// Display independent, mode dependent values
float[] brightnessLevelsNits;
+ float[] brightnessLevels = null;
float[] luxLevels;
if (isForIdleMode) {
brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
@@ -130,11 +131,21 @@ public abstract class BrightnessMappingStrategy {
} else {
brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
+
+ brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels();
+ if (brightnessLevels == null || brightnessLevels.length == 0) {
+ // Load the old configuration in the range [0, 255]. The values need to be
+ // normalized to the range [0, 1].
+ int[] brightnessLevelsInt = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ brightnessLevels = new float[brightnessLevelsInt.length];
+ for (int i = 0; i < brightnessLevels.length; i++) {
+ brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]);
+ }
+ }
}
// Display independent, mode independent values
- int[] brightnessLevelsBacklight = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
@@ -155,8 +166,8 @@ public abstract class BrightnessMappingStrategy {
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
autoBrightnessAdjustmentMaxGamma, isForIdleMode, displayWhiteBalanceController);
- } else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
- return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
+ } else if (isValidMapping(luxLevels, brightnessLevels)) {
+ return new SimpleMappingStrategy(luxLevels, brightnessLevels,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
} else {
return null;
@@ -620,7 +631,7 @@ public abstract class BrightnessMappingStrategy {
private float mUserBrightness;
private long mShortTermModelTimeout;
- private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma,
+ private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
long timeout) {
Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
"Lux and brightness arrays must not be empty!");
@@ -635,7 +646,7 @@ public abstract class BrightnessMappingStrategy {
mBrightness = new float[N];
for (int i = 0; i < N; i++) {
mLux[i] = lux[i];
- mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]);
+ mBrightness[i] = brightness[i];
}
mMaxGamma = maxGamma;
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 70d4ad2c6e1f..c26118eac7a2 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.os.Handler;
import android.view.Display;
+import com.android.server.display.feature.DisplayManagerFlags;
+
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,6 +41,7 @@ abstract class DisplayAdapter {
private final Handler mHandler;
private final Listener mListener;
private final String mName;
+ private final DisplayManagerFlags mFeatureFlags;
public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
@@ -50,13 +53,14 @@ abstract class DisplayAdapter {
private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode.
// Called with SyncRoot lock held.
- public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, String name) {
+ DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
+ Listener listener, String name, DisplayManagerFlags featureFlags) {
mSyncRoot = syncRoot;
mContext = context;
mHandler = handler;
mListener = listener;
mName = name;
+ mFeatureFlags = featureFlags;
}
/**
@@ -88,6 +92,10 @@ abstract class DisplayAdapter {
return mName;
}
+ public final DisplayManagerFlags getFeatureFlags() {
+ return mFeatureFlags;
+ }
+
/**
* Registers the display adapter with the display manager.
*
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2fdf90d7d286..3b05b47eb542 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -400,6 +400,7 @@ abstract class DisplayDevice {
}
private DisplayDeviceConfig loadDisplayDeviceConfig() {
- return DisplayDeviceConfig.create(mContext, false);
+ return DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false,
+ mDisplayAdapter.getFeatureFlags());
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index b99de5cc0c7b..d97127c91fbf 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,7 @@ import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
+import com.android.server.display.config.LuxToBrightnessMapping;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
import com.android.server.display.config.Point;
@@ -77,6 +78,7 @@ import com.android.server.display.config.ThermalThrottling;
import com.android.server.display.config.ThresholdPoint;
import com.android.server.display.config.UsiVersion;
import com.android.server.display.config.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -310,16 +312,18 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <darkeningLightDebounceIdleMillis>
* 1000
* </darkeningLightDebounceIdleMillis>
- * <displayBrightnessMapping>
- * <displayBrightnessPoint>
- * <lux>50</lux>
- * <nits>45.32</nits>
- * </displayBrightnessPoint>
- * <displayBrightnessPoint>
- * <lux>80</lux>
- * <nits>75.43</nits>
- * </displayBrightnessPoint>
- * </displayBrightnessMapping>
+ * <luxToBrightnessMapping>
+ * <map>
+ * <point>
+ * <first>0</first>
+ * <second>0.2</second>
+ * </point>
+ * <point>
+ * <first>80</first>
+ * <second>0.3</second>
+ * </point>
+ * </map>
+ * </luxToBrightnessMapping>
* </autoBrightness>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -528,6 +532,7 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <majorVersion>2</majorVersion>
* <minorVersion>0</minorVersion>
* </usiVersion>
+ * <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
* </displayConfiguration>
* }
* </pre>
@@ -629,7 +634,6 @@ public class DisplayDeviceConfig {
// for the corresponding values above
private float[] mBrightness;
-
/**
* Array of desired screen brightness in nits corresponding to the lux values
* in the mBrightnessLevelsLux array. The display brightness is defined as the
@@ -639,20 +643,25 @@ public class DisplayDeviceConfig {
private float[] mBrightnessLevelsNits;
/**
- * Array of light sensor lux values to define our levels for auto backlight
- * brightness support.
+ * Array of desired screen brightness corresponding to the lux values
+ * in the mBrightnessLevelsLux array. The brightness values must be non-negative and
+ * non-decreasing. They must be between {@link PowerManager.BRIGHTNESS_MIN} and
+ * {@link PowerManager.BRIGHTNESS_MAX}. This must be overridden in platform specific overlays
+ */
+ private float[] mBrightnessLevels;
+
+ /**
+ * Array of light sensor lux values to define our levels for auto-brightness support.
*
- * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits,
- * with first value always being 0 lux
+ * The first lux value is always 0.
*
- * The control points must be strictly increasing. Each control point
- * corresponds to an entry in the brightness backlight values arrays.
- * For example, if lux == level[1] (second element of the levels array)
- * then the brightness will be determined by value[0] (first element
- * of the brightness values array).
+ * The control points must be strictly increasing. Each control point corresponds to an entry
+ * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
+ * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
+ * element of the brightness values array).
*
- * Spline interpolation is used to determine the auto-brightness
- * backlight values for lux levels between these control points.
+ * Spline interpolation is used to determine the auto-brightness values for lux levels between
+ * these control points.
*/
private float[] mBrightnessLevelsLux;
@@ -843,9 +852,17 @@ public class DisplayDeviceConfig {
@Nullable
private HdrBrightnessData mHdrBrightnessData;
+ /**
+ * Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ private float mBrightnessCapForWearBedtimeMode;
+
+ private final DisplayManagerFlags mFlags;
+
@VisibleForTesting
- DisplayDeviceConfig(Context context) {
+ DisplayDeviceConfig(Context context, DisplayManagerFlags flags) {
mContext = context;
+ mFlags = flags;
}
/**
@@ -861,9 +878,9 @@ public class DisplayDeviceConfig {
* @return A configuration instance for the specified display.
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
- boolean isFirstDisplay) {
+ boolean isFirstDisplay, DisplayManagerFlags flags) {
final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
- isFirstDisplay);
+ isFirstDisplay, flags);
config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
return config;
@@ -878,28 +895,29 @@ public class DisplayDeviceConfig {
* or the default values.
* @return A configuration instance.
*/
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml,
+ DisplayManagerFlags flags) {
final DisplayDeviceConfig config;
if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
+ config = getConfigFromGlobalXml(context, flags);
} else {
- config = getConfigFromPmValues(context);
+ config = getConfigFromPmValues(context, flags);
}
return config;
}
private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
@@ -907,7 +925,7 @@ public class DisplayDeviceConfig {
// If no config can be loaded from any ddc xml at all,
// prepare a whole config using the global config.xml.
// Guaranteed not null
- return create(context, isFirstDisplay);
+ return create(context, isFirstDisplay, flags);
}
private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
@@ -960,18 +978,19 @@ public class DisplayDeviceConfig {
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
- File baseDirectory, long physicalDisplayId) {
+ File baseDirectory, long physicalDisplayId, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
// Create config using filename from physical ID (including "stable" bit).
config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
// Create config using filename from physical ID (excluding "stable" bit).
final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
- config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
+ config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag,
+ flags);
if (config != null) {
return config;
}
@@ -980,7 +999,7 @@ public class DisplayDeviceConfig {
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
int port = physicalAddress.getPort();
- config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
+ config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port, flags);
return config;
}
@@ -1599,6 +1618,13 @@ public class DisplayDeviceConfig {
}
/**
+ * @return Auto brightness brightening levels
+ */
+ public float[] getAutoBrightnessBrighteningLevels() {
+ return mBrightnessLevels;
+ }
+
+ /**
* @return Default peak refresh rate of the associated display
*/
public int getDefaultPeakRefreshRate() {
@@ -1741,6 +1767,13 @@ public class DisplayDeviceConfig {
return mHostUsiVersion;
}
+ /**
+ * @return Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ public float getBrightnessCapForWearBedtimeMode() {
+ return mBrightnessCapForWearBedtimeMode;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1844,6 +1877,7 @@ public class DisplayDeviceConfig {
+ mAutoBrightnessDarkeningLightDebounceIdle
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels)
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
+ ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+ "\n"
@@ -1867,36 +1901,39 @@ public class DisplayDeviceConfig {
+ ", mHighAmbientBrightnessThresholds= "
+ Arrays.toString(mHighAmbientBrightnessThresholds)
+ "\n"
- + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
+ + "mScreenOffBrightnessSensorValueToLux= " + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ "\n"
+ "mUsiVersion= " + mHostUsiVersion + "\n"
- + "mHdrBrightnessData" + mHdrBrightnessData
+ + "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+ + "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
+ "}";
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
- String suffixFormat, long idNumber) {
+ String suffixFormat, long idNumber, DisplayManagerFlags flags) {
final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
- final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ final DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
if (config.initFromFile(filePath)) {
return config;
}
return null;
}
- private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromGlobalXml(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromGlobalXml();
return config;
}
- private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromPmValues(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromDefaultValues();
return config;
}
@@ -1938,6 +1975,7 @@ public class DisplayDeviceConfig {
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
loadUsiVersion(config);
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
+ loadBrightnessCapForWearBedtimeMode(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -1961,6 +1999,7 @@ public class DisplayDeviceConfig {
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
loadRefreshRateSetting(null);
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -2599,8 +2638,23 @@ public class DisplayDeviceConfig {
* loading the value from the display config, and if not present, falls back to config.xml.
*/
private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
- if (autoBrightnessConfig == null
- || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
+ if (mFlags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
+ && autoBrightnessConfig.getLuxToBrightnessMapping() != null) {
+ LuxToBrightnessMapping mapping = autoBrightnessConfig.getLuxToBrightnessMapping();
+ final int size = mapping.getMap().getPoint().size();
+ mBrightnessLevels = new float[size];
+ mBrightnessLevelsLux = new float[size];
+ for (int i = 0; i < size; i++) {
+ float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
+ mBrightnessLevels[i] = mBacklightToBrightnessSpline.interpolate(backlight);
+ mBrightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
+ .floatValue();
+ }
+ if (size > 0 && mBrightnessLevelsLux[0] != 0) {
+ throw new IllegalArgumentException(
+ "The first lux value in the display brightness mapping must be 0");
+ }
+ } else {
mBrightnessLevelsNits = getFloatArray(mContext.getResources()
.obtainTypedArray(com.android.internal.R.array
.config_autoBrightnessDisplayValuesNits), PowerManager
@@ -2608,18 +2662,6 @@ public class DisplayDeviceConfig {
mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
.getIntArray(com.android.internal.R.array
.config_autoBrightnessLevels));
- } else {
- final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().size();
- mBrightnessLevelsNits = new float[size];
- // The first control point is implicit and always at 0 lux.
- mBrightnessLevelsLux = new float[size + 1];
- for (int i = 0; i < size; i++) {
- mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getNits().floatValue();
- mBrightnessLevelsLux[i + 1] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getLux().floatValue();
- }
}
}
@@ -3350,6 +3392,23 @@ public class DisplayDeviceConfig {
: null;
}
+ private void loadBrightnessCapForWearBedtimeMode(DisplayConfiguration config) {
+ if (config != null) {
+ BigDecimal configBrightnessCap = config.getScreenBrightnessCapForWearBedtimeMode();
+ if (configBrightnessCap != null) {
+ mBrightnessCapForWearBedtimeMode = configBrightnessCap.floatValue();
+ } else {
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
+ }
+ }
+ }
+
+ private void loadBrightnessCapForWearBedtimeModeFromConfigXml() {
+ mBrightnessCapForWearBedtimeMode = BrightnessSynchronizer.brightnessIntToFloat(
+ mContext.getResources().getInteger(com.android.internal.R.integer
+ .config_screenBrightnessCapForWearBedtimeMode));
+ }
+
/**
* Container for high brightness mode configuration data.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a7c17d54980..2ab15e639d68 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1853,7 +1853,7 @@ public final class DisplayManagerService extends SystemService {
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo);
+ mHandler, mDisplayDeviceRepo, mFlags);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
@@ -1871,7 +1871,7 @@ public final class DisplayManagerService extends SystemService {
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler, mFlags));
}
private void registerWifiDisplayAdapterLocked() {
@@ -1880,7 +1880,7 @@ public final class DisplayManagerService extends SystemService {
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
- mPersistentDataStore);
+ mPersistentDataStore, mFlags);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
}
@@ -3288,8 +3288,10 @@ public final class DisplayManagerService extends SystemService {
@VisibleForTesting
static class Injector {
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ flags);
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index ff9a1ab61b13..22898a65c5de 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -84,8 +84,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private final boolean mIsBootDisplayModeSupported;
- private final DisplayManagerFlags mFlags;
-
private final DisplayNotificationManager mDisplayNotificationManager;
private Context mOverlayContext;
@@ -103,12 +101,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
Listener listener, DisplayManagerFlags flags,
DisplayNotificationManager displayNotificationManager,
Injector injector) {
- super(syncRoot, context, handler, listener, TAG);
+ super(syncRoot, context, handler, listener, TAG, flags);
mDisplayNotificationManager = displayNotificationManager;
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
- mFlags = flags;
}
@Override
@@ -510,7 +507,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// Load display device config
final Context context = getOverlayContext();
mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
- mIsFirstDisplay);
+ mIsFirstDisplay, getFeatureFlags());
// Load brightness HWC quirk
mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -831,7 +828,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
+ ", state=" + Display.stateToString(state) + ")");
}
- boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+ boolean isDisplayOffloadEnabled =
+ getFeatureFlags().isDisplayOffloadEnabled();
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
@@ -1377,8 +1375,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay, flags);
}
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2ce7690ecc3f..22ff2d0eeffd 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -36,6 +36,7 @@ import android.view.SurfaceControl;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import java.io.PrintWriter;
@@ -134,8 +135,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
// Called with SyncRoot lock held.
public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, Handler uiHandler) {
- super(syncRoot, context, handler, listener, TAG);
+ Context context, Handler handler, Listener listener, Handler uiHandler,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mUiHandler = uiHandler;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index edbd42465534..ec5ad7de11b3 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -61,6 +61,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -88,7 +89,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener) {
+ Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) {
this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
@Override
public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
@@ -99,14 +100,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
public void destroyDisplay(IBinder displayToken) {
DisplayControl.destroyDisplay(displayToken);
}
- });
+ }, featureFlags);
}
@VisibleForTesting
VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
- super(syncRoot, context, handler, listener, TAG);
+ SurfaceControlDisplayFactory surfaceControlDisplayFactory,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mHandler = handler;
mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 7660cf8a1c3a..aa98cd85d38e 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -41,6 +41,7 @@ import android.view.SurfaceControl;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import java.io.PrintWriter;
@@ -99,8 +100,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
// Called with SyncRoot lock held.
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- PersistentDataStore persistentDataStore) {
- super(syncRoot, context, handler, listener, TAG);
+ PersistentDataStore persistentDataStore, DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
throw new RuntimeException("WiFi display was requested, "
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index dfcda40d8e3c..8a884b6221a2 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness.clamper;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
@@ -31,6 +32,18 @@ abstract class BrightnessClamper<T> {
protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
protected boolean mIsActive = false;
+ @NonNull
+ protected final Handler mHandler;
+
+ @NonNull
+ protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+ BrightnessClamper(Handler handler,
+ BrightnessClamperController.ClamperChangeListener changeListener) {
+ mHandler = handler;
+ mChangeListener = changeListener;
+ }
+
float getBrightnessCap() {
return mBrightnessCap;
}
@@ -60,6 +73,7 @@ abstract class BrightnessClamper<T> {
protected enum Type {
THERMAL,
- POWER
+ POWER,
+ BEDTIME_MODE
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index b57491949196..765608e88356 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -90,7 +90,8 @@ public class BrightnessClamperController {
}
};
- mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags);
+ mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+ context);
mModifiers = injector.getModifiers(context);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -234,7 +235,7 @@ public class BrightnessClamperController {
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, Context context) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
clampers.add(
new BrightnessThermalClamper(handler, clamperChangeListener, data));
@@ -242,6 +243,10 @@ public class BrightnessClamperController {
clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
data));
}
+ if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
+ clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
+ clamperChangeListener, data));
+ }
return clampers;
}
@@ -257,7 +262,8 @@ public class BrightnessClamperController {
* Config Data for clampers
*/
public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
- BrightnessPowerClamper.PowerData {
+ BrightnessPowerClamper.PowerData,
+ BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
private final String mUniqueDisplayId;
@NonNull
@@ -315,5 +321,10 @@ public class BrightnessClamperController {
public PowerThrottlingConfigData getPowerThrottlingConfigData() {
return mDisplayDeviceConfig.getPowerThrottlingConfigData();
}
+
+ @Override
+ public float getBrightnessWearBedtimeModeCap() {
+ return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 339b5896f3f7..790322d75251 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -49,10 +49,6 @@ class BrightnessPowerClamper extends
private final Injector mInjector;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangeListener;
@Nullable
private PmicMonitor mPmicMonitor;
// data from DeviceConfig, for all displays, for all dataSets
@@ -99,10 +95,9 @@ class BrightnessPowerClamper extends
@VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
PowerData powerData) {
+ super(handler, listener);
mInjector = injector;
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangeListener = listener;
mHandler.post(() -> {
setDisplayData(powerData);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 8ae962b83c69..944a8a65693b 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -54,10 +54,6 @@ class BrightnessThermalClamper extends
private final IThermalService mThermalService;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangelistener;
// data from DeviceConfig, for all displays, for all dataSets
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
@NonNull
@@ -108,10 +104,9 @@ class BrightnessThermalClamper extends
@VisibleForTesting
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
+ super(handler, listener);
mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangelistener = listener;
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
@@ -220,7 +215,7 @@ class BrightnessThermalClamper extends
if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
mBrightnessCap = brightnessCap;
mIsActive = isActive;
- mChangelistener.onChanged();
+ mChangeListener.onChanged();
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
new file mode 100644
index 000000000000..7e853bfc4ad6
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BrightnessWearBedtimeModeClamper extends
+ BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> {
+
+ public static final int BEDTIME_MODE_OFF = 0;
+ public static final int BEDTIME_MODE_ON = 1;
+
+ private final Context mContext;
+
+ private final ContentObserver mSettingsObserver;
+
+ BrightnessWearBedtimeModeClamper(Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ this(new Injector(), handler, context, listener, data);
+ }
+
+ @VisibleForTesting
+ BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ super(handler, listener);
+ mContext = context;
+ mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final int bedtimeModeSetting = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.Wearable.BEDTIME_MODE,
+ BEDTIME_MODE_OFF);
+ mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
+ mChangeListener.onChanged();
+ }
+ };
+ injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
+ }
+
+ @NonNull
+ @Override
+ Type getType() {
+ return Type.BEDTIME_MODE;
+ }
+
+ @Override
+ void onDeviceConfigChanged() {}
+
+ @Override
+ void onDisplayChanged(WearBedtimeModeData displayData) {
+ mHandler.post(() -> {
+ mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap();
+ mChangeListener.onChanged();
+ });
+ }
+
+ @Override
+ void stop() {
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+
+ interface WearBedtimeModeData {
+ float getBrightnessWearBedtimeModeCap();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
+ /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
+ }
+ }
+}
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 5cfbf26338e3..c71f0cf2dd91 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -90,6 +90,14 @@ public class DisplayManagerFlags {
Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
Flags::enableExternalVsyncProximityVote);
+ private final FlagState mBrightnessWearBedtimeModeClamperFlagState = new FlagState(
+ Flags.FLAG_BRIGHTNESS_WEAR_BEDTIME_MODE_CLAMPER,
+ Flags::brightnessWearBedtimeModeClamper);
+
+ private final FlagState mAutoBrightnessModesFlagState = new FlagState(
+ Flags.FLAG_AUTO_BRIGHTNESS_MODES,
+ Flags::autoBrightnessModes);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -178,6 +186,17 @@ public class DisplayManagerFlags {
return mVsyncProximityVote.isEnabled();
}
+ public boolean isBrightnessWearBedtimeModeClamperEnabled() {
+ return mBrightnessWearBedtimeModeClamperFlagState.isEnabled();
+ }
+
+ /**
+ * @return Whether generic auto-brightness modes are enabled
+ */
+ public boolean areAutoBrightnessModesEnabled() {
+ return mAutoBrightnessModesFlagState.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -197,6 +216,8 @@ public class DisplayManagerFlags {
pw.println(" " + mSmallAreaDetectionFlagState);
pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
pw.println(" " + mVsyncProximityVote);
+ pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
+ pw.println(" " + mAutoBrightnessModesFlagState);
}
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 d95bdae7514d..9dfa1ee41ad6 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
@@ -120,3 +120,19 @@ flag {
bug: "284866750"
is_fixed_read_only: true
}
+
+flag {
+ name: "brightness_wear_bedtime_mode_clamper"
+ namespace: "display_manager"
+ description: "Feature flag for the Wear Bedtime mode brightness clamper"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "auto_brightness_modes"
+ namespace: "display_manager"
+ description: "Feature flag for generic auto-brightness modes"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 8eb03ec0d3bd..8fa838de3327 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -21,8 +21,8 @@ import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT
import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
import static android.view.Display.Mode.INVALID_MODE_ID;
-import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRate;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -925,16 +925,11 @@ public class DisplayModeDirector {
}
@VisibleForTesting
- DisplayObserver getDisplayObserver() {
- return mDisplayObserver;
- }
-
- @VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
synchronized (mLock) {
- mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate,
- defaultRefreshRate, Display.DEFAULT_DISPLAY);
+ mSettingsObserver.updateRefreshRateSettingLocked(
+ minRefreshRate, peakRefreshRate, defaultRefreshRate);
return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
}
}
@@ -1268,23 +1263,9 @@ public class DisplayModeDirector {
mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
}
- /**
- * Update refresh rate settings for all displays
- */
private void updateRefreshRateSettingLocked() {
- Display[] displays = mInjector.getDisplays();
- for (int i = 0; i < displays.length; i++) {
- updateRefreshRateSettingLocked(displays[i].getDisplayId());
- }
- }
-
- /**
- * Update refresh rate settings for a specific display
- * @param displayId The display ID
- */
- private void updateRefreshRateSettingLocked(int displayId) {
final ContentResolver cr = mContext.getContentResolver();
- float highestRefreshRate = findHighestRefreshRate(mContext, displayId);
+ float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
float minRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
@@ -1312,12 +1293,11 @@ public class DisplayModeDirector {
}
}
- updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
- displayId);
+ updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
- private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
- float defaultRefreshRate, int displayId) {
+ private void updateRefreshRateSettingLocked(
+ float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
// TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
// used to predict if we're going to be doing frequent refresh rate switching, and if
// so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1325,9 +1305,9 @@ public class DisplayModeDirector {
Vote peakVote = peakRefreshRate == 0f
? null
: Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
peakVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
Vote defaultVote =
defaultRefreshRate == 0f
@@ -1484,8 +1464,7 @@ public class DisplayModeDirector {
}
}
- @VisibleForTesting
- public final class DisplayObserver implements DisplayManager.DisplayListener {
+ private final class DisplayObserver implements DisplayManager.DisplayListener {
// Note that we can never call into DisplayManager or any of the non-POD classes it
// returns, while holding mLock since it may call into DMS, which might be simultaneously
// calling into us already holding its own lock.
@@ -1577,7 +1556,6 @@ public class DisplayModeDirector {
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
- mSettingsObserver.updateRefreshRateSettingLocked(displayId);
}
@Nullable
diff --git a/services/core/java/com/android/server/flags/OWNERS b/services/core/java/com/android/server/flags/OWNERS
new file mode 100644
index 000000000000..535a7509601c
--- /dev/null
+++ b/services/core/java/com/android/server/flags/OWNERS
@@ -0,0 +1 @@
+per-file pinner.aconfig = edgararriaga@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/flags/pinner.aconfig b/services/core/java/com/android/server/flags/pinner.aconfig
new file mode 100644
index 000000000000..606a6be29511
--- /dev/null
+++ b/services/core/java/com/android/server/flags/pinner.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+ name: "pin_webview"
+ namespace: "system_performance"
+ description: "This flag controls if webview should be pinned in memory."
+ bug: "307594624"
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 6f9b7d6328c7..27b01a5424b8 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -1048,10 +1049,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
stopBatching();
if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
- // change period and/or lowPowerMode
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, mProviderRequest.isLowPower())) {
- Log.e(TAG, "set_position_mode failed in updateRequirements");
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (!positionMode.equals(mLastPositionMode)) {
+ stopNavigating();
+ startNavigating();
+ }
+ } else {
+ // change period and/or lowPowerMode
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, mProviderRequest.isLowPower())) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
+ }
}
} else if (!mStarted) {
// start GPS
@@ -1234,11 +1247,32 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- interval, mProviderRequest.isLowPower())) {
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
+
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ boolean success = mGnssNative.setPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (success) {
+ mLastPositionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ } else {
+ mLastPositionMode = null;
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ } else {
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ interval, mProviderRequest.isLowPower())) {
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
}
if (!mGnssNative.start()) {
setStarted(false);
diff --git a/services/core/java/com/android/server/media/AudioAttributesUtils.java b/services/core/java/com/android/server/media/AudioAttributesUtils.java
deleted file mode 100644
index 8cb334dc2260..000000000000
--- a/services/core/java/com/android/server/media/AudioAttributesUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.AudioAttributes;
-import android.media.AudioDeviceAttributes;
-import android.media.AudioDeviceInfo;
-import android.media.MediaRoute2Info;
-
-import com.android.media.flags.Flags;
-
-/* package */ final class AudioAttributesUtils {
-
- /* package */ static final AudioAttributes ATTRIBUTES_MEDIA = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .build();
-
- private AudioAttributesUtils() {
- // no-op to prevent instantiation.
- }
-
- @MediaRoute2Info.Type
- /* package */ static int mapToMediaRouteType(
- @NonNull AudioDeviceAttributes audioDeviceAttributes) {
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- return MediaRoute2Info.TYPE_HDMI_ARC;
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- return MediaRoute2Info.TYPE_HDMI_EARC;
- }
- }
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
- case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
- return MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
- case AudioDeviceInfo.TYPE_WIRED_HEADSET:
- return MediaRoute2Info.TYPE_WIRED_HEADSET;
- case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
- return MediaRoute2Info.TYPE_WIRED_HEADPHONES;
- case AudioDeviceInfo.TYPE_DOCK:
- case AudioDeviceInfo.TYPE_DOCK_ANALOG:
- return MediaRoute2Info.TYPE_DOCK;
- case AudioDeviceInfo.TYPE_HDMI:
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- return MediaRoute2Info.TYPE_HDMI;
- case AudioDeviceInfo.TYPE_USB_DEVICE:
- return MediaRoute2Info.TYPE_USB_DEVICE;
- case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
- return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- case AudioDeviceInfo.TYPE_BLE_HEADSET:
- return MediaRoute2Info.TYPE_BLE_HEADSET;
- case AudioDeviceInfo.TYPE_HEARING_AID:
- return MediaRoute2Info.TYPE_HEARING_AID;
- default:
- return MediaRoute2Info.TYPE_UNKNOWN;
- }
- }
-
- /* package */ static boolean isDeviceOutputAttributes(
- @Nullable AudioDeviceAttributes audioDeviceAttributes) {
- if (audioDeviceAttributes == null) {
- return false;
- }
-
- if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
- return false;
- }
-
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
- case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
- case AudioDeviceInfo.TYPE_WIRED_HEADSET:
- case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
- case AudioDeviceInfo.TYPE_DOCK:
- case AudioDeviceInfo.TYPE_DOCK_ANALOG:
- case AudioDeviceInfo.TYPE_HDMI:
- case AudioDeviceInfo.TYPE_HDMI_ARC:
- case AudioDeviceInfo.TYPE_HDMI_EARC:
- case AudioDeviceInfo.TYPE_USB_DEVICE:
- return true;
- default:
- return false;
- }
- }
-
- /* package */ static boolean isBluetoothOutputAttributes(
- @Nullable AudioDeviceAttributes audioDeviceAttributes) {
- if (audioDeviceAttributes == null) {
- return false;
- }
-
- if (audioDeviceAttributes.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
- return false;
- }
-
- switch (audioDeviceAttributes.getType()) {
- case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
- case AudioDeviceInfo.TYPE_BLE_HEADSET:
- case AudioDeviceInfo.TYPE_BLE_SPEAKER:
- case AudioDeviceInfo.TYPE_HEARING_AID:
- return true;
- default:
- return false;
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
index 8bc69c226d1a..a00999d08b5b 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -17,7 +17,6 @@
package com.android.server.media;
import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
-import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,38 +30,37 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioManager;
-import android.media.AudioSystem;
import android.media.MediaRoute2Info;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
- * Controls bluetooth routes and provides selected route override.
+ * Maintains a list of connected {@link BluetoothDevice bluetooth devices} and allows their
+ * activation.
*
- * <p>The controller offers similar functionality to {@link LegacyBluetoothRouteController} but does
- * not support routes selection logic. Instead, relies on external clients to make a decision
- * about currently selected route.
- *
- * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
- * Policies.
+ * <p>This class also serves as ground truth for assigning {@link MediaRoute2Info#getId() route ids}
+ * for bluetooth routes via {@link #getRouteIdForBluetoothAddress}.
*/
-/* package */ class AudioPoliciesBluetoothRouteController
- implements BluetoothRouteController {
- private static final String TAG = "APBtRouteController";
+// TODO: b/305199571 - Rename this class to remove the RouteController suffix, which causes
+// confusion with the BluetoothRouteController interface.
+/* package */ class AudioPoliciesBluetoothRouteController {
+ private static final String TAG = SystemMediaRoute2Provider.TAG;
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
@@ -75,11 +73,8 @@ import java.util.Set;
private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
new DeviceStateChangedReceiver();
- @NonNull
- private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
-
- @NonNull
- private final SparseIntArray mVolumeMap = new SparseIntArray();
+ @NonNull private Map<String, BluetoothDevice> mAddressToBondedDevice = new HashMap<>();
+ @NonNull private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
@NonNull
private final Context mContext;
@@ -89,11 +84,6 @@ import java.util.Set;
private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
@NonNull
private final BluetoothProfileMonitor mBluetoothProfileMonitor;
- @NonNull
- private final AudioManager mAudioManager;
-
- @Nullable
- private BluetoothRouteInfo mSelectedBluetoothRoute;
AudioPoliciesBluetoothRouteController(@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@@ -107,21 +97,12 @@ import java.util.Set;
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(bluetoothAdapter);
- Objects.requireNonNull(bluetoothProfileMonitor);
- Objects.requireNonNull(listener);
-
- mContext = context;
- mBluetoothAdapter = bluetoothAdapter;
- mBluetoothProfileMonitor = bluetoothProfileMonitor;
- mAudioManager = mContext.getSystemService(AudioManager.class);
- mListener = listener;
-
- updateBluetoothRoutes();
+ mContext = Objects.requireNonNull(context);
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
+ mBluetoothProfileMonitor = Objects.requireNonNull(bluetoothProfileMonitor);
+ mListener = Objects.requireNonNull(listener);
}
- @Override
public void start(UserHandle user) {
mBluetoothProfileMonitor.start();
@@ -133,122 +114,63 @@ import java.util.Set;
IntentFilter deviceStateChangedIntentFilter = new IntentFilter();
- deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
deviceStateChangedIntentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
deviceStateChangedIntentFilter.addAction(
BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
deviceStateChangedIntentFilter.addAction(
BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
- deviceStateChangedIntentFilter.addAction(
- BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
mContext.registerReceiverAsUser(mDeviceStateChangedReceiver, user,
deviceStateChangedIntentFilter, null, null);
+ updateBluetoothRoutes();
}
- @Override
public void stop() {
mContext.unregisterReceiver(mAdapterStateChangedReceiver);
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
- @Override
- public boolean selectRoute(@Nullable String deviceAddress) {
- synchronized (this) {
- // Fetch all available devices in order to avoid race conditions with Bluetooth stack.
- updateBluetoothRoutes();
-
- if (deviceAddress == null) {
- mSelectedBluetoothRoute = null;
- return true;
- }
-
- BluetoothRouteInfo bluetoothRouteInfo = mBluetoothRoutes.get(deviceAddress);
-
- if (bluetoothRouteInfo == null) {
- Slog.w(TAG, "Cannot find bluetooth route for " + deviceAddress);
- return false;
- }
-
- mSelectedBluetoothRoute = bluetoothRouteInfo;
- setRouteConnectionState(mSelectedBluetoothRoute, STATE_CONNECTED);
-
- updateConnectivityStateForDevicesInTheSameGroup();
-
- return true;
- }
- }
-
- /**
- * Updates connectivity state for devices in the same devices group.
- *
- * <p>{@link BluetoothProfile#LE_AUDIO} and {@link BluetoothProfile#HEARING_AID} support
- * grouping devices. Devices that belong to the same group should have the same routeId but
- * different physical address.
- *
- * <p>In case one of the devices from the group is selected then other devices should also
- * reflect this by changing their connectivity status to
- * {@link MediaRoute2Info#CONNECTION_STATE_CONNECTED}.
- */
- private void updateConnectivityStateForDevicesInTheSameGroup() {
- synchronized (this) {
- for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRoute.mRoute.getId(), mSelectedBluetoothRoute.mRoute.getId())
- && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
- mSelectedBluetoothRoute.mBtDevice.getAddress())) {
- setRouteConnectionState(btRoute, STATE_CONNECTED);
- }
- }
- }
+ @Nullable
+ public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) {
+ BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address);
+ // TODO: b/305199571 - Optimize the following statement to avoid creating the full
+ // MediaRoute2Info instance. We just need the id.
+ return bluetoothDevice != null
+ ? createBluetoothRoute(bluetoothDevice).mRoute.getId()
+ : null;
}
- @Override
- public void transferTo(@Nullable String routeId) {
- if (routeId == null) {
- mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
- return;
- }
-
- BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId);
+ public synchronized void activateBluetoothDeviceWithAddress(String address) {
+ BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(address);
if (btRouteInfo == null) {
- Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId);
+ Slog.w(TAG, "activateBluetoothDeviceWithAddress: Ignoring unknown address " + address);
return;
}
-
mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
}
- @Nullable
- private BluetoothRouteInfo findBluetoothRouteWithRouteId(@Nullable String routeId) {
- if (routeId == null) {
- return null;
- }
- synchronized (this) {
- for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
- return btRouteInfo;
- }
- }
- }
- return null;
- }
-
private void updateBluetoothRoutes() {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
- if (bondedDevices == null) {
- return;
- }
-
synchronized (this) {
mBluetoothRoutes.clear();
-
- // We need to query all available to BT stack devices in order to avoid inconsistency
- // between external services, like, AndroidManager, and BT stack.
+ if (bondedDevices == null) {
+ // Bonded devices is null upon running into a BluetoothAdapter error.
+ Log.w(TAG, "BluetoothAdapter.getBondedDevices returned null.");
+ return;
+ }
+ // We don't clear bonded devices if we receive a null getBondedDevices result, because
+ // that probably means that the bluetooth stack ran into an issue. Not that all devices
+ // have been unpaired.
+ mAddressToBondedDevice =
+ bondedDevices.stream()
+ .collect(
+ Collectors.toMap(
+ BluetoothDevice::getAddress, Function.identity()));
for (BluetoothDevice device : bondedDevices) {
- if (isDeviceConnected(device)) {
+ if (device.isConnected()) {
BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
if (newBtRoute.mConnectedProfiles.size() > 0) {
mBluetoothRoutes.put(device.getAddress(), newBtRoute);
@@ -258,106 +180,51 @@ import java.util.Set;
}
}
- @VisibleForTesting
- /* package */ boolean isDeviceConnected(@NonNull BluetoothDevice device) {
- return device.isConnected();
- }
-
- @Nullable
- @Override
- public MediaRoute2Info getSelectedRoute() {
- synchronized (this) {
- if (mSelectedBluetoothRoute == null) {
- return null;
- }
-
- return mSelectedBluetoothRoute.mRoute;
- }
- }
-
@NonNull
- @Override
- public List<MediaRoute2Info> getTransferableRoutes() {
- List<MediaRoute2Info> routes = getAllBluetoothRoutes();
- synchronized (this) {
- if (mSelectedBluetoothRoute != null) {
- routes.remove(mSelectedBluetoothRoute.mRoute);
- }
- }
- return routes;
- }
-
- @NonNull
- @Override
- public List<MediaRoute2Info> getAllBluetoothRoutes() {
+ public List<MediaRoute2Info> getAvailableBluetoothRoutes() {
List<MediaRoute2Info> routes = new ArrayList<>();
- List<String> routeIds = new ArrayList<>();
-
- MediaRoute2Info selectedRoute = getSelectedRoute();
- if (selectedRoute != null) {
- routes.add(selectedRoute);
- routeIds.add(selectedRoute.getId());
- }
+ Set<String> routeIds = new HashSet<>();
synchronized (this) {
for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- // A pair of hearing aid devices or having the same hardware address
- if (routeIds.contains(btRoute.mRoute.getId())) {
- continue;
+ // See createBluetoothRoute for info on why we do this.
+ if (routeIds.add(btRoute.mRoute.getId())) {
+ routes.add(btRoute.mRoute);
}
- routes.add(btRoute.mRoute);
- routeIds.add(btRoute.mRoute.getId());
}
}
return routes;
}
- @Override
- public boolean updateVolumeForDevices(int devices, int volume) {
- int routeType;
- if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
- routeType = MediaRoute2Info.TYPE_HEARING_AID;
- } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
- | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
- | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
- routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) {
- routeType = MediaRoute2Info.TYPE_BLE_HEADSET;
- } else {
- return false;
- }
-
- synchronized (this) {
- mVolumeMap.put(routeType, volume);
- if (mSelectedBluetoothRoute == null
- || mSelectedBluetoothRoute.mRoute.getType() != routeType) {
- return false;
- }
-
- mSelectedBluetoothRoute.mRoute =
- new MediaRoute2Info.Builder(mSelectedBluetoothRoute.mRoute)
- .setVolume(volume)
- .build();
- }
-
- notifyBluetoothRoutesUpdated();
- return true;
- }
-
private void notifyBluetoothRoutesUpdated() {
mListener.onBluetoothRoutesUpdated();
}
+ /**
+ * Creates a new {@link BluetoothRouteInfo}, including its member {@link
+ * BluetoothRouteInfo#mRoute}.
+ *
+ * <p>The most important logic in this method is around the {@link MediaRoute2Info#getId() route
+ * id} assignment. In some cases we want to group multiple {@link BluetoothDevice bluetooth
+ * devices} as a single media route. For example, the left and right hearing aids get exposed as
+ * two different BluetoothDevice instances, but we want to show them as a single route. In this
+ * case, we assign the same route id to all "group" bluetooth devices (like left and right
+ * hearing aids), so that a single route is exposed for both of them.
+ *
+ * <p>Deduplication by id happens downstream because we need to be able to refer to all
+ * bluetooth devices individually, since the audio stack refers to a bluetooth device group by
+ * any of its member devices.
+ */
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
BluetoothRouteInfo
newBtRoute = new BluetoothRouteInfo();
newBtRoute.mBtDevice = device;
-
- String routeId = device.getAddress();
String deviceName = device.getName();
if (TextUtils.isEmpty(deviceName)) {
deviceName = mContext.getResources().getText(R.string.unknownName).toString();
}
+
+ String routeId = device.getAddress();
int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
newBtRoute.mConnectedProfiles = new SparseBooleanArray();
if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.A2DP, device)) {
@@ -365,7 +232,6 @@ import java.util.Set;
}
if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.HEARING_AID, device)) {
newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
- // Intentionally assign the same ID for a pair of devices to publish only one of them.
routeId = HEARING_AID_ROUTE_ID_PREFIX
+ mBluetoothProfileMonitor.getGroupId(BluetoothProfile.HEARING_AID, device);
type = MediaRoute2Info.TYPE_HEARING_AID;
@@ -377,66 +243,27 @@ import java.util.Set;
type = MediaRoute2Info.TYPE_BLE_HEADSET;
}
- // Current volume will be set when connected.
- newBtRoute.mRoute = new MediaRoute2Info.Builder(routeId, deviceName)
- .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
- .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
- .setDescription(mContext.getResources().getText(
- R.string.bluetooth_a2dp_audio_route_name).toString())
- .setType(type)
- .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setAddress(device.getAddress())
- .build();
+ // Note that volume is only relevant for active bluetooth routes, and those are managed via
+ // AudioManager.
+ newBtRoute.mRoute =
+ new MediaRoute2Info.Builder(routeId, deviceName)
+ .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
+ .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
+ .setDescription(
+ mContext.getResources()
+ .getText(R.string.bluetooth_a2dp_audio_route_name)
+ .toString())
+ .setType(type)
+ .setAddress(device.getAddress())
+ .build();
return newBtRoute;
}
- private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
- @MediaRoute2Info.ConnectionState int state) {
- if (btRoute == null) {
- Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
- return;
- }
- if (btRoute.mRoute.getConnectionState() == state) {
- return;
- }
-
- MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.mRoute)
- .setConnectionState(state);
- builder.setType(btRoute.getRouteType());
-
-
-
- if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
- int currentVolume;
- synchronized (this) {
- currentVolume = mVolumeMap.get(btRoute.getRouteType(), 0);
- }
- builder.setVolume(currentVolume);
- }
-
- btRoute.mRoute = builder.build();
- }
-
private static class BluetoothRouteInfo {
private BluetoothDevice mBtDevice;
private MediaRoute2Info mRoute;
private SparseBooleanArray mConnectedProfiles;
-
- @MediaRoute2Info.Type
- int getRouteType() {
- // Let hearing aid profile have a priority.
- if (mConnectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
- return MediaRoute2Info.TYPE_HEARING_AID;
- }
-
- if (mConnectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) {
- return MediaRoute2Info.TYPE_BLE_HEADSET;
- }
-
- return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
- }
}
private class AdapterStateChangedReceiver extends BroadcastReceiver {
@@ -468,9 +295,6 @@ import java.util.Set;
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
- case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
- case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
- case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
index 6bdfae2dc02f..27df00f30531 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -17,228 +17,590 @@
package com.android.server.media;
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
-import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
-import static android.media.MediaRoute2Info.TYPE_DOCK;
-import static android.media.MediaRoute2Info.TYPE_HDMI;
-import static android.media.MediaRoute2Info.TYPE_HDMI_ARC;
-import static android.media.MediaRoute2Info.TYPE_HDMI_EARC;
-import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.IAudioService;
import android.media.MediaRoute2Info;
-import android.os.RemoteException;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.media.BluetoothRouteController.NoOpBluetoothRouteController;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+/**
+ * Maintains a list of all available routes and supports transfers to any of them.
+ *
+ * <p>This implementation is intended for use in conjunction with {@link
+ * NoOpBluetoothRouteController}, as it manages bluetooth devices directly.
+ *
+ * <p>This implementation obtains and manages all routes via {@link AudioManager}, with the
+ * exception of {@link AudioManager#handleBluetoothActiveDeviceChanged inactive bluetooth} routes
+ * which are managed by {@link AudioPoliciesBluetoothRouteController}, which depends on the
+ * bluetooth stack (for example {@link BluetoothAdapter}.
+ */
+// TODO: b/305199571 - Rename this class to avoid the AudioPolicies prefix, which has been flagged
+// by the audio team as a confusing name.
/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+ private static final String TAG = SystemMediaRoute2Provider.TAG;
- private static final String TAG = "APDeviceRoutesController";
-
- @NonNull
- private final Context mContext;
@NonNull
- private final AudioManager mAudioManager;
- @NonNull
- private final IAudioService mAudioService;
+ private static final AudioAttributes MEDIA_USAGE_AUDIO_ATTRIBUTES =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
@NonNull
- private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
- @NonNull
- private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+ private static final SparseArray<SystemRouteInfo> AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO =
+ new SparseArray<>();
- private int mDeviceVolume;
+ @NonNull private final Context mContext;
+ @NonNull private final AudioManager mAudioManager;
+ @NonNull private final Handler mHandler;
+ @NonNull private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ @NonNull private final AudioPoliciesBluetoothRouteController mBluetoothRouteController;
@NonNull
- private MediaRoute2Info mDeviceRoute;
- @Nullable
- private MediaRoute2Info mSelectedRoute;
+ private final Map<String, MediaRoute2InfoHolder> mRouteIdToAvailableDeviceRoutes =
+ new HashMap<>();
+
+ @NonNull private final AudioProductStrategy mStrategyForMedia;
- @VisibleForTesting
- /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context,
+ @NonNull private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallbackImpl();
+
+ @NonNull
+ private final AudioManager.OnDevicesForAttributesChangedListener
+ mOnDevicesForAttributesChangedListener = this::onDevicesForAttributesChangedListener;
+
+ @NonNull private MediaRoute2Info mSelectedRoute;
+
+ // TODO: b/305199571 - Support nullable btAdapter and strategyForMedia which, when null, means
+ // no support for transferring to inactive bluetooth routes and transferring to any routes
+ // respectively.
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ /* package */ AudioPoliciesDeviceRouteController(
+ @NonNull Context context,
@NonNull AudioManager audioManager,
- @NonNull IAudioService audioService,
+ @NonNull Looper looper,
+ @NonNull AudioProductStrategy strategyForMedia,
+ @NonNull BluetoothAdapter btAdapter,
@NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(audioManager);
- Objects.requireNonNull(audioService);
- Objects.requireNonNull(onDeviceRouteChangedListener);
+ mContext = Objects.requireNonNull(context);
+ mAudioManager = Objects.requireNonNull(audioManager);
+ mHandler = new Handler(Objects.requireNonNull(looper));
+ mStrategyForMedia = Objects.requireNonNull(strategyForMedia);
+ mOnDeviceRouteChangedListener = Objects.requireNonNull(onDeviceRouteChangedListener);
+ mBluetoothRouteController =
+ new AudioPoliciesBluetoothRouteController(
+ mContext, btAdapter, this::rebuildAvailableRoutes);
+ rebuildAvailableRoutes();
+ }
- mContext = context;
- mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public void start(UserHandle mUser) {
+ mBluetoothRouteController.start(mUser);
+ mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, mHandler);
+ mAudioManager.addOnDevicesForAttributesChangedListener(
+ AudioRoutingUtils.ATTRIBUTES_MEDIA,
+ new HandlerExecutor(mHandler),
+ mOnDevicesForAttributesChangedListener);
+ }
- mAudioManager = audioManager;
- mAudioService = audioService;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public void stop() {
+ mAudioManager.removeOnDevicesForAttributesChangedListener(
+ mOnDevicesForAttributesChangedListener);
+ mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
+ mBluetoothRouteController.stop();
+ mHandler.removeCallbacksAndMessages(/* token= */ null);
+ }
- AudioRoutesInfo newAudioRoutes = null;
- try {
- newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
- } catch (RemoteException e) {
- Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
- }
+ @Override
+ @NonNull
+ public synchronized MediaRoute2Info getSelectedRoute() {
+ return mSelectedRoute;
+ }
- mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ @Override
+ @NonNull
+ public synchronized List<MediaRoute2Info> getAvailableRoutes() {
+ return mRouteIdToAvailableDeviceRoutes.values().stream()
+ .map(it -> it.mMediaRoute2Info)
+ .toList();
}
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Override
- public synchronized boolean selectRoute(@Nullable Integer type) {
- if (type == null) {
- mSelectedRoute = null;
- return true;
+ public synchronized void transferTo(@Nullable String routeId) {
+ if (routeId == null) {
+ // This should never happen: This branch should only execute when the matching bluetooth
+ // route controller is not the no-op one.
+ // TODO: b/305199571 - Make routeId non-null and remove this branch once we remove the
+ // legacy route controller implementations.
+ Slog.e(TAG, "Unexpected call to AudioPoliciesDeviceRouteController#transferTo(null)");
+ return;
}
-
- if (!isDeviceRouteType(type)) {
- return false;
+ MediaRoute2InfoHolder mediaRoute2InfoHolder = mRouteIdToAvailableDeviceRoutes.get(routeId);
+ if (mediaRoute2InfoHolder == null) {
+ Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId);
+ return;
+ }
+ if (mediaRoute2InfoHolder.mCorrespondsToInactiveBluetoothRoute) {
+ // By default, the last connected device is the active route so we don't need to apply a
+ // routing audio policy.
+ mBluetoothRouteController.activateBluetoothDeviceWithAddress(
+ mediaRoute2InfoHolder.mMediaRoute2Info.getAddress());
+ mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
+ } else {
+ AudioDeviceAttributes attr =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ mediaRoute2InfoHolder.mAudioDeviceInfoType,
+ /* address= */ ""); // This is not a BT device, hence no address needed.
+ mAudioManager.setPreferredDeviceForStrategy(mStrategyForMedia, attr);
}
+ }
- mSelectedRoute = createRouteFromAudioInfo(type);
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public synchronized boolean updateVolume(int volume) {
+ // TODO: b/305199571 - Optimize so that we only update the volume of the selected route. We
+ // don't need to rebuild all available routes.
+ rebuildAvailableRoutes();
return true;
}
- @Override
- @NonNull
- public synchronized MediaRoute2Info getSelectedRoute() {
- if (mSelectedRoute != null) {
- return mSelectedRoute;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ private void onDevicesForAttributesChangedListener(
+ AudioAttributes attributes, List<AudioDeviceAttributes> unusedAudioDeviceAttributes) {
+ if (attributes.getUsage() == AudioAttributes.USAGE_MEDIA) {
+ // We only care about the media usage. Ignore everything else.
+ rebuildAvailableRoutes();
}
- return mDeviceRoute;
}
- @Override
- public synchronized boolean updateVolume(int volume) {
- if (mDeviceVolume == volume) {
- return false;
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ private synchronized void rebuildAvailableRoutes() {
+ List<AudioDeviceAttributes> attributesOfSelectedOutputDevices =
+ mAudioManager.getDevicesForAttributes(MEDIA_USAGE_AUDIO_ATTRIBUTES);
+ int selectedDeviceAttributesType;
+ if (attributesOfSelectedOutputDevices.isEmpty()) {
+ Slog.e(
+ TAG,
+ "Unexpected empty list of output devices for media. Using built-in speakers.");
+ selectedDeviceAttributesType = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ } else {
+ if (attributesOfSelectedOutputDevices.size() > 1) {
+ Slog.w(
+ TAG,
+ "AudioManager.getDevicesForAttributes returned more than one element. Using"
+ + " the first one.");
+ }
+ selectedDeviceAttributesType = attributesOfSelectedOutputDevices.get(0).getType();
}
- mDeviceVolume = volume;
-
- if (mSelectedRoute != null) {
- mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute)
- .setVolume(volume)
- .build();
+ AudioDeviceInfo[] audioDeviceInfos =
+ mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ mRouteIdToAvailableDeviceRoutes.clear();
+ MediaRoute2InfoHolder newSelectedRouteHolder = null;
+ for (AudioDeviceInfo audioDeviceInfo : audioDeviceInfos) {
+ MediaRoute2Info mediaRoute2Info =
+ createMediaRoute2InfoFromAudioDeviceInfo(audioDeviceInfo);
+ // Null means audioDeviceInfo is not a supported media output, like a phone's builtin
+ // earpiece. We ignore those.
+ if (mediaRoute2Info != null) {
+ int audioDeviceInfoType = audioDeviceInfo.getType();
+ MediaRoute2InfoHolder newHolder =
+ MediaRoute2InfoHolder.createForAudioManagerRoute(
+ mediaRoute2Info, audioDeviceInfoType);
+ mRouteIdToAvailableDeviceRoutes.put(mediaRoute2Info.getId(), newHolder);
+ if (selectedDeviceAttributesType == audioDeviceInfoType) {
+ newSelectedRouteHolder = newHolder;
+ }
+ }
}
- mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
- .setVolume(volume)
- .build();
+ if (mRouteIdToAvailableDeviceRoutes.isEmpty()) {
+ // Due to an unknown reason (possibly an audio server crash), we ended up with an empty
+ // list of routes. Our entire codebase assumes at least one system route always exists,
+ // so we create a placeholder route represented as a built-in speaker for
+ // user-presentation purposes.
+ Slog.e(TAG, "Ended up with an empty list of routes. Creating a placeholder route.");
+ MediaRoute2InfoHolder placeholderRouteHolder = createPlaceholderBuiltinSpeakerRoute();
+ String placeholderRouteId = placeholderRouteHolder.mMediaRoute2Info.getId();
+ mRouteIdToAvailableDeviceRoutes.put(placeholderRouteId, placeholderRouteHolder);
+ }
- return true;
+ if (newSelectedRouteHolder == null) {
+ Slog.e(
+ TAG,
+ "Could not map this selected device attribute type to an available route: "
+ + selectedDeviceAttributesType);
+ // We know mRouteIdToAvailableDeviceRoutes is not empty.
+ newSelectedRouteHolder = mRouteIdToAvailableDeviceRoutes.values().iterator().next();
+ }
+ MediaRoute2InfoHolder selectedRouteHolderWithUpdatedVolumeInfo =
+ newSelectedRouteHolder.copyWithVolumeInfoFromAudioManager(mAudioManager);
+ mRouteIdToAvailableDeviceRoutes.put(
+ newSelectedRouteHolder.mMediaRoute2Info.getId(),
+ selectedRouteHolderWithUpdatedVolumeInfo);
+ mSelectedRoute = selectedRouteHolderWithUpdatedVolumeInfo.mMediaRoute2Info;
+
+ // We only add those BT routes that we have not already obtained from audio manager (which
+ // are active).
+ mBluetoothRouteController.getAvailableBluetoothRoutes().stream()
+ .filter(it -> !mRouteIdToAvailableDeviceRoutes.containsKey(it.getId()))
+ .map(MediaRoute2InfoHolder::createForInactiveBluetoothRoute)
+ .forEach(
+ it -> mRouteIdToAvailableDeviceRoutes.put(it.mMediaRoute2Info.getId(), it));
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged();
}
- @NonNull
- private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
- int type = TYPE_BUILTIN_SPEAKER;
-
- if (newRoutes != null) {
- if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
- type = TYPE_WIRED_HEADPHONES;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
- type = TYPE_WIRED_HEADSET;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
- type = TYPE_DOCK;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
- type = TYPE_HDMI;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
- type = TYPE_USB_DEVICE;
- }
- }
+ private MediaRoute2InfoHolder createPlaceholderBuiltinSpeakerRoute() {
+ int type = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ return MediaRoute2InfoHolder.createForAudioManagerRoute(
+ createMediaRoute2Info(
+ /* routeId= */ null, type, /* productName= */ null, /* address= */ null),
+ type);
+ }
- return createRouteFromAudioInfo(type);
+ @Nullable
+ private MediaRoute2Info createMediaRoute2InfoFromAudioDeviceInfo(
+ AudioDeviceInfo audioDeviceInfo) {
+ String address = audioDeviceInfo.getAddress();
+ // Passing a null route id means we want to get the default id for the route. Generally, we
+ // only expect to pass null for non-Bluetooth routes.
+ String routeId =
+ TextUtils.isEmpty(address)
+ ? null
+ : mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
+ return createMediaRoute2Info(
+ routeId, audioDeviceInfo.getType(), audioDeviceInfo.getProductName(), address);
}
- @NonNull
- private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) {
- int name = R.string.default_audio_route_name;
- switch (type) {
- case TYPE_WIRED_HEADPHONES:
- case TYPE_WIRED_HEADSET:
- name = R.string.default_audio_route_name_headphones;
- break;
- case TYPE_DOCK:
- name = R.string.default_audio_route_name_dock_speakers;
- break;
- case TYPE_HDMI:
- case TYPE_HDMI_ARC:
- case TYPE_HDMI_EARC:
- name = R.string.default_audio_route_name_external_device;
- break;
- case TYPE_USB_DEVICE:
- name = R.string.default_audio_route_name_usb;
- break;
- }
-
- synchronized (this) {
- return new MediaRoute2Info.Builder(
- MediaRoute2Info.ROUTE_ID_DEVICE,
- mContext.getResources().getText(name).toString())
- .setVolumeHandling(
- mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
+ /**
+ * Creates a new {@link MediaRoute2Info} using the provided information.
+ *
+ * @param routeId A route id, or null to use an id pre-defined for the given {@code type}.
+ * @param audioDeviceInfoType The type as obtained from {@link AudioDeviceInfo#getType}.
+ * @param productName The product name as obtained from {@link
+ * AudioDeviceInfo#getProductName()}, or null to use a predefined name for the given {@code
+ * type}.
+ * @param address The type as obtained from {@link AudioDeviceInfo#getAddress()} or {@link
+ * BluetoothDevice#getAddress()}.
+ * @return The new {@link MediaRoute2Info}.
+ */
+ @Nullable
+ private MediaRoute2Info createMediaRoute2Info(
+ @Nullable String routeId,
+ int audioDeviceInfoType,
+ @Nullable CharSequence productName,
+ @Nullable String address) {
+ SystemRouteInfo systemRouteInfo =
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.get(audioDeviceInfoType);
+ if (systemRouteInfo == null) {
+ // Device type that's intentionally unsupported for media output, like the built-in
+ // earpiece.
+ return null;
+ }
+ CharSequence humanReadableName = productName;
+ if (TextUtils.isEmpty(humanReadableName)) {
+ humanReadableName = mContext.getResources().getText(systemRouteInfo.mNameResource);
+ }
+ if (routeId == null) {
+ // The caller hasn't provided an id, so we use a pre-defined one. This happens when we
+ // are creating a non-BT route, or we are creating a BT route but a race condition
+ // caused AudioManager to expose the BT route before BluetoothAdapter, preventing us
+ // from getting an id using BluetoothRouteController#getRouteIdForBluetoothAddress.
+ routeId = systemRouteInfo.mDefaultRouteId;
}
+ return new MediaRoute2Info.Builder(routeId, humanReadableName)
+ .setType(systemRouteInfo.mMediaRoute2InfoType)
+ .setAddress(address)
+ .setSystemRoute(true)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+ .build();
}
/**
- * Checks if the given type is a device route.
- *
- * <p>Device route means a route which is either built-in or wired to the current device.
- *
- * @param type specifies the type of the device.
- * @return {@code true} if the device is wired or built-in and {@code false} otherwise.
+ * Holds a {@link MediaRoute2Info} and associated information that we don't want to put in the
+ * {@link MediaRoute2Info} class because it's solely necessary for the implementation of this
+ * class.
*/
- private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) {
- switch (type) {
- case TYPE_BUILTIN_SPEAKER:
- case TYPE_WIRED_HEADPHONES:
- case TYPE_WIRED_HEADSET:
- case TYPE_DOCK:
- case TYPE_HDMI:
- case TYPE_HDMI_ARC:
- case TYPE_HDMI_EARC:
- case TYPE_USB_DEVICE:
- return true;
- default:
- return false;
+ private static class MediaRoute2InfoHolder {
+
+ public final MediaRoute2Info mMediaRoute2Info;
+ public final int mAudioDeviceInfoType;
+ public final boolean mCorrespondsToInactiveBluetoothRoute;
+
+ public static MediaRoute2InfoHolder createForAudioManagerRoute(
+ MediaRoute2Info mediaRoute2Info, int audioDeviceInfoType) {
+ return new MediaRoute2InfoHolder(
+ mediaRoute2Info,
+ audioDeviceInfoType,
+ /* correspondsToInactiveBluetoothRoute= */ false);
+ }
+
+ public static MediaRoute2InfoHolder createForInactiveBluetoothRoute(
+ MediaRoute2Info mediaRoute2Info) {
+ // There's no corresponding audio device info, hence the audio device info type is
+ // unknown.
+ return new MediaRoute2InfoHolder(
+ mediaRoute2Info,
+ /* audioDeviceInfoType= */ AudioDeviceInfo.TYPE_UNKNOWN,
+ /* correspondsToInactiveBluetoothRoute= */ true);
+ }
+
+ private MediaRoute2InfoHolder(
+ MediaRoute2Info mediaRoute2Info,
+ int audioDeviceInfoType,
+ boolean correspondsToInactiveBluetoothRoute) {
+ mMediaRoute2Info = mediaRoute2Info;
+ mAudioDeviceInfoType = audioDeviceInfoType;
+ mCorrespondsToInactiveBluetoothRoute = correspondsToInactiveBluetoothRoute;
+ }
+
+ public MediaRoute2InfoHolder copyWithVolumeInfoFromAudioManager(
+ AudioManager mAudioManager) {
+ MediaRoute2Info routeInfoWithVolumeInfo =
+ new MediaRoute2Info.Builder(mMediaRoute2Info)
+ .setVolumeHandling(
+ mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
+ .setVolumeMax(
+ mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .build();
+ return new MediaRoute2InfoHolder(
+ routeInfoWithVolumeInfo,
+ mAudioDeviceInfoType,
+ mCorrespondsToInactiveBluetoothRoute);
}
}
- private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+ /**
+ * Holds route information about an {@link AudioDeviceInfo#getType() audio device info type}.
+ */
+ private static class SystemRouteInfo {
+ /** The type to use for {@link MediaRoute2Info#getType()}. */
+ public final int mMediaRoute2InfoType;
+
+ /**
+ * Holds the route id to use if no other id is provided.
+ *
+ * <p>We only expect this id to be used for non-bluetooth routes. For bluetooth routes, in a
+ * normal scenario, the id is generated from the device information (like address, or
+ * hiSyncId), and this value is ignored. A non-normal scenario may occur when there's race
+ * condition between {@link BluetoothAdapter} and {@link AudioManager}, who are not
+ * synchronized.
+ */
+ public final String mDefaultRouteId;
+
+ /**
+ * The name to use for {@link MediaRoute2Info#getName()}.
+ *
+ * <p>Usually replaced by the UI layer with a localized string.
+ */
+ public final int mNameResource;
+
+ private SystemRouteInfo(int mediaRoute2InfoType, String defaultRouteId, int nameResource) {
+ mMediaRoute2InfoType = mediaRoute2InfoType;
+ mDefaultRouteId = defaultRouteId;
+ mNameResource = nameResource;
+ }
+ }
+ private class AudioDeviceCallbackImpl extends AudioDeviceCallback {
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
@Override
- public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
- boolean isDeviceRouteChanged;
- MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
-
- synchronized (AudioPoliciesDeviceRouteController.this) {
- mDeviceRoute = deviceRoute;
- isDeviceRouteChanged = mSelectedRoute == null;
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ for (AudioDeviceInfo deviceInfo : addedDevices) {
+ if (AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.contains(deviceInfo.getType())) {
+ // When a new valid media output is connected, we clear any routing policies so
+ // that the default routing logic from the audio framework kicks in. As a result
+ // of this, when the user connects a bluetooth device or a wired headset, the
+ // new device becomes the active route, which is the traditional behavior.
+ mAudioManager.removePreferredDeviceForStrategy(mStrategyForMedia);
+ rebuildAvailableRoutes();
+ break;
+ }
}
+ }
- if (isDeviceRouteChanged) {
- mOnDeviceRouteChangedListener.onDeviceRouteChanged();
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE
+ })
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ for (AudioDeviceInfo deviceInfo : removedDevices) {
+ if (AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.contains(deviceInfo.getType())) {
+ rebuildAvailableRoutes();
+ break;
+ }
}
}
}
+ static {
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
+ /* defaultRouteId= */ "ROUTE_ID_BUILTIN_SPEAKER",
+ /* nameResource= */ R.string.default_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_WIRED_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_WIRED_HEADSET",
+ /* nameResource= */ R.string.default_audio_route_name_headphones));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_WIRED_HEADPHONES,
+ /* defaultRouteId= */ "ROUTE_ID_WIRED_HEADPHONES",
+ /* nameResource= */ R.string.default_audio_route_name_headphones));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLUETOOTH_A2DP,
+ /* defaultRouteId= */ "ROUTE_ID_BLUETOOTH_A2DP",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_DOCK,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_DOCK,
+ /* defaultRouteId= */ "ROUTE_ID_DOCK",
+ /* nameResource= */ R.string.default_audio_route_name_dock_speakers));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_USB_DEVICE,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_USB_DEVICE,
+ /* defaultRouteId= */ "ROUTE_ID_USB_DEVICE",
+ /* nameResource= */ R.string.default_audio_route_name_usb));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_USB_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_USB_HEADSET",
+ /* nameResource= */ R.string.default_audio_route_name_usb));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI_ARC,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI_ARC,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI_ARC",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HDMI_EARC,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HDMI_EARC,
+ /* defaultRouteId= */ "ROUTE_ID_HDMI_EARC",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ // TODO: b/305199571 - Add a proper type constants and human readable names for AUX_LINE,
+ // LINE_ANALOG, LINE_DIGITAL, BLE_BROADCAST, BLE_SPEAKER, BLE_HEADSET, and HEARING_AID.
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_HEARING_AID,
+ /* defaultRouteId= */ "ROUTE_ID_HEARING_AID",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_BLE_HEADSET",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET, // TODO: b/305199571 - Make a new type.
+ /* defaultRouteId= */ "ROUTE_ID_BLE_SPEAKER",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_BLE_HEADSET,
+ /* defaultRouteId= */ "ROUTE_ID_BLE_BROADCAST",
+ /* nameResource= */ R.string.bluetooth_a2dp_audio_route_name));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_LINE_DIGITAL,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_LINE_DIGITAL",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_LINE_ANALOG,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_LINE_ANALOG",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_AUX_LINE,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_UNKNOWN,
+ /* defaultRouteId= */ "ROUTE_ID_AUX_LINE",
+ /* nameResource= */ R.string.default_audio_route_name_external_device));
+ AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+ AudioDeviceInfo.TYPE_DOCK_ANALOG,
+ new SystemRouteInfo(
+ MediaRoute2Info.TYPE_DOCK,
+ /* defaultRouteId= */ "ROUTE_ID_DOCK_ANALOG",
+ /* nameResource= */ R.string.default_audio_route_name_dock_speakers));
+ }
}
diff --git a/services/core/java/com/android/server/media/AudioRoutingUtils.java b/services/core/java/com/android/server/media/AudioRoutingUtils.java
new file mode 100644
index 000000000000..13f11eb80ece
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioRoutingUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioProductStrategy;
+
+/** Holds utils related to routing in the audio framework. */
+/* package */ final class AudioRoutingUtils {
+
+ /* package */ static final AudioAttributes ATTRIBUTES_MEDIA =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Nullable
+ /* package */ static AudioProductStrategy getMediaAudioProductStrategy() {
+ for (AudioProductStrategy strategy : AudioManager.getAudioProductStrategies()) {
+ if (strategy.supportsAudioAttributes(AudioRoutingUtils.ATTRIBUTES_MEDIA)) {
+ return strategy;
+ }
+ }
+ return null;
+ }
+
+ private AudioRoutingUtils() {
+ // no-op to prevent instantiation.
+ }
+}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java
index 2b01001fd7d1..74fdf6ee1d7f 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteController.java
@@ -44,19 +44,11 @@ import java.util.Objects;
@NonNull
static BluetoothRouteController createInstance(@NonNull Context context,
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
- Objects.requireNonNull(context);
Objects.requireNonNull(listener);
+ BluetoothAdapter btAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
- BluetoothManager bluetoothManager = (BluetoothManager)
- context.getSystemService(Context.BLUETOOTH_SERVICE);
- BluetoothAdapter btAdapter = bluetoothManager.getAdapter();
-
- if (btAdapter == null) {
+ if (btAdapter == null || Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
return new NoOpBluetoothRouteController();
- }
-
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesBluetoothRouteController(context, btAdapter, listener);
} else {
return new LegacyBluetoothRouteController(context, btAdapter, listener);
}
@@ -74,17 +66,6 @@ import java.util.Objects;
*/
void stop();
-
- /**
- * Selects the route with the given {@code deviceAddress}.
- *
- * @param deviceAddress The physical address of the device to select. May be null to unselect
- * the currently selected device.
- * @return Whether the selection succeeds. If the selection fails, the state of the instance
- * remains unaltered.
- */
- boolean selectRoute(@Nullable String deviceAddress);
-
/**
* Transfers Bluetooth output to the given route.
*
@@ -158,12 +139,6 @@ import java.util.Objects;
}
@Override
- public boolean selectRoute(String deviceAddress) {
- // no op
- return false;
- }
-
- @Override
public void transferTo(String routeId) {
// no op
}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 0fdaaa7604e5..9f175a9a0277 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -16,17 +16,25 @@
package com.android.server.media;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.media.AudioManager;
-import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Looper;
import android.os.ServiceManager;
+import android.os.UserHandle;
import com.android.media.flags.Flags;
+import java.util.List;
+
/**
* Controls device routes.
*
@@ -37,44 +45,65 @@ import com.android.media.flags.Flags;
*/
/* package */ interface DeviceRouteController {
- /**
- * Returns a new instance of {@link DeviceRouteController}.
- */
- /* package */ static DeviceRouteController createInstance(@NonNull Context context,
+ /** Returns a new instance of {@link DeviceRouteController}. */
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ /* package */ static DeviceRouteController createInstance(
+ @NonNull Context context,
+ @NonNull Looper looper,
@NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
AudioManager audioManager = context.getSystemService(AudioManager.class);
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.getService(Context.AUDIO_SERVICE));
+ AudioProductStrategy strategyForMedia = AudioRoutingUtils.getMediaAudioProductStrategy();
- if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesDeviceRouteController(context,
+ BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
+ BluetoothAdapter btAdapter =
+ bluetoothManager != null ? bluetoothManager.getAdapter() : null;
+
+ // TODO: b/305199571 - Make the audio policies implementation work without the need for a
+ // bluetooth adapter or a strategy for media. If no strategy for media is available we can
+ // disallow media router transfers, and without a bluetooth adapter we can remove support
+ // for transfers to inactive bluetooth routes.
+ if (strategyForMedia != null
+ && btAdapter != null
+ && Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ return new AudioPoliciesDeviceRouteController(
+ context,
audioManager,
- audioService,
+ looper,
+ strategyForMedia,
+ btAdapter,
onDeviceRouteChangedListener);
} else {
- return new LegacyDeviceRouteController(context,
- audioManager,
- audioService,
- onDeviceRouteChangedListener);
+ IAudioService audioService =
+ IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
+ return new LegacyDeviceRouteController(
+ context, audioManager, audioService, onDeviceRouteChangedListener);
}
}
+ /** Returns the currently selected device (built-in or wired) route. */
+ @NonNull
+ MediaRoute2Info getSelectedRoute();
+
/**
- * Select the route with the given built-in or wired {@link MediaRoute2Info.Type}.
- *
- * <p>If the type is {@code null} then unselects the route and falls back to the default device
- * route observed from
- * {@link com.android.server.audio.AudioService#startWatchingRoutes(IAudioRoutesObserver)}.
+ * Returns all available routes.
*
- * @param type device type. May be {@code null} to unselect currently selected route.
- * @return whether the selection succeeds. If the selection fails the state of the controller
- * remains intact.
+ * <p>Note that this method returns available routes including the selected route because (a)
+ * this interface doesn't guarantee that the internal state of the controller won't change
+ * between calls to {@link #getSelectedRoute()} and this method and (b) {@link
+ * #getSelectedRoute()} may be treated as a transferable route (not a selected route) if the
+ * selected route is from {@link BluetoothRouteController}.
*/
- boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
+ List<MediaRoute2Info> getAvailableRoutes();
- /** Returns the currently selected device (built-in or wired) route. */
- @NonNull
- MediaRoute2Info getSelectedRoute();
+ /**
+ * Transfers device output to the given route.
+ *
+ * <p>If the route is {@code null} then active route will be deactivated.
+ *
+ * @param routeId to switch to or {@code null} to unset the active device.
+ */
+ void transferTo(@Nullable String routeId);
/**
* Updates device route volume.
@@ -85,6 +114,18 @@ import com.android.media.flags.Flags;
boolean updateVolume(int volume);
/**
+ * Starts listening for changes in the system to keep an up to date view of available and
+ * selected devices.
+ */
+ void start(UserHandle mUser);
+
+ /**
+ * Stops keeping the internal state up to date with the system, releasing any resources acquired
+ * in {@link #start}
+ */
+ void stop();
+
+ /**
* Interface for receiving events when device route has changed.
*/
interface OnDeviceRouteChangedListener {
diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
index ba3cecf7c091..041fceaf8d3d 100644
--- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java
@@ -132,12 +132,6 @@ class LegacyBluetoothRouteController implements BluetoothRouteController {
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
- @Override
- public boolean selectRoute(String deviceAddress) {
- // No-op as the class decides if a route is selected based on Bluetooth events.
- return false;
- }
-
/**
* Transfers to a given bluetooth route.
* The dedicated BT device with the route would be activated.
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
index 65874e23dcdc..c0f28346705c 100644
--- a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -35,11 +35,13 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -73,7 +75,6 @@ import java.util.Objects;
private int mDeviceVolume;
private MediaRoute2Info mDeviceRoute;
- @VisibleForTesting
/* package */ LegacyDeviceRouteController(@NonNull Context context,
@NonNull AudioManager audioManager,
@NonNull IAudioService audioService,
@@ -100,9 +101,13 @@ import java.util.Objects;
}
@Override
- public boolean selectRoute(@Nullable Integer type) {
- // No-op as the controller does not support selection from the outside of the class.
- return false;
+ public void start(UserHandle mUser) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do.
}
@Override
@@ -112,6 +117,17 @@ import java.util.Objects;
}
@Override
+ public synchronized List<MediaRoute2Info> getAvailableRoutes() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public synchronized void transferTo(@Nullable String routeId) {
+ // Unsupported. This implementation doesn't support transferable routes (always exposes a
+ // single non-bluetooth route).
+ }
+
+ @Override
public synchronized boolean updateVolume(int volume) {
if (mDeviceVolume == volume) {
return false;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index c8dba800a017..86d78334d546 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -16,15 +16,12 @@
package com.android.server.media;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioAttributes;
-import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
@@ -51,7 +48,8 @@ import java.util.Set;
*/
// TODO: check thread safety. We may need to use lock to protect variables.
class SystemMediaRoute2Provider extends MediaRoute2Provider {
- private static final String TAG = "MR2SystemProvider";
+ // Package-visible to use this tag for all system routing logic (done across multiple classes).
+ /* package */ static final String TAG = "MR2SystemProvider";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final ComponentName COMPONENT_NAME = new ComponentName(
@@ -77,26 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
private final AudioManagerBroadcastReceiver mAudioReceiver =
new AudioManagerBroadcastReceiver();
- private final AudioManager.OnDevicesForAttributesChangedListener
- mOnDevicesForAttributesChangedListener =
- new AudioManager.OnDevicesForAttributesChangedListener() {
- @Override
- public void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
- @NonNull List<AudioDeviceAttributes> devices) {
- if (attributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
- return;
- }
-
- mHandler.post(() -> {
- updateSelectedAudioDevice(devices);
- notifyProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- }
- };
-
private final Object mRequestLock = new Object();
@GuardedBy("mRequestLock")
private volatile SessionCreationRequest mPendingSessionCreationRequest;
@@ -106,7 +84,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mIsSystemRouteProvider = true;
mContext = context;
mUser = user;
- mHandler = new Handler(Looper.getMainLooper());
+ Looper looper = Looper.getMainLooper();
+ mHandler = new Handler(looper);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -123,25 +102,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mDeviceRouteController =
DeviceRouteController.createInstance(
context,
- () -> {
- mHandler.post(
- () -> {
- publishProviderState();
- if (updateSessionInfosIfNeeded()) {
- notifySessionInfoUpdated();
- }
- });
- });
-
- mAudioManager.addOnDevicesForAttributesChangedListener(
- AudioAttributesUtils.ATTRIBUTES_MEDIA, mContext.getMainExecutor(),
- mOnDevicesForAttributesChangedListener);
-
- // These methods below should be called after all fields are initialized, as they
- // access the fields inside.
- List<AudioDeviceAttributes> devices =
- mAudioManager.getDevicesForAttributes(AudioAttributesUtils.ATTRIBUTES_MEDIA);
- updateSelectedAudioDevice(devices);
+ looper,
+ () ->
+ mHandler.post(
+ () -> {
+ publishProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
+ }));
updateProviderState();
updateSessionInfosIfNeeded();
}
@@ -151,20 +120,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
intentFilter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
mContext.registerReceiverAsUser(mAudioReceiver, mUser,
intentFilter, null, null);
-
- mHandler.post(() -> {
- mBluetoothRouteController.start(mUser);
- notifyProviderState();
- });
- updateVolume();
+ mHandler.post(
+ () -> {
+ mDeviceRouteController.start(mUser);
+ mBluetoothRouteController.start(mUser);
+ });
}
public void stop() {
mContext.unregisterReceiver(mAudioReceiver);
- mHandler.post(() -> {
- mBluetoothRouteController.stop();
- notifyProviderState();
- });
+ mHandler.post(
+ () -> {
+ mBluetoothRouteController.stop();
+ mDeviceRouteController.stop();
+ notifyProviderState();
+ });
}
@Override
@@ -225,13 +195,26 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
public void transferToRoute(long requestId, String sessionId, String routeId) {
if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
// The currently selected route is the default route.
+ Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
return;
}
-
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
- if (TextUtils.equals(routeId, selectedDeviceRoute.getId())) {
+ boolean isAvailableDeviceRoute =
+ mDeviceRouteController.getAvailableRoutes().stream()
+ .anyMatch(it -> it.getId().equals(routeId));
+ boolean isSelectedDeviceRoute = TextUtils.equals(routeId, selectedDeviceRoute.getId());
+
+ if (isSelectedDeviceRoute || isAvailableDeviceRoute) {
+ // The requested route is managed by the device route controller. Note that the selected
+ // device route doesn't necessarily match mSelectedRouteId (which is the selected route
+ // of the routing session). If the selected device route is transferred to, we need to
+ // make the bluetooth routes inactive so that the device route becomes the selected
+ // route of the routing session.
+ mDeviceRouteController.transferTo(routeId);
mBluetoothRouteController.transferTo(null);
} else {
+ // The requested route is managed by the bluetooth route controller.
+ mDeviceRouteController.transferTo(null);
mBluetoothRouteController.transferTo(routeId);
}
}
@@ -280,33 +263,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
MediaRoute2Info selectedDeviceRoute = mDeviceRouteController.getSelectedRoute();
- RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- SYSTEM_SESSION_ID, packageName).setSystemSession(true);
+ RoutingSessionInfo.Builder builder =
+ new RoutingSessionInfo.Builder(SYSTEM_SESSION_ID, packageName)
+ .setSystemSession(true);
builder.addSelectedRoute(selectedDeviceRoute.getId());
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addTransferableRoute(route.getId());
}
- return builder.setProviderId(mUniqueId).build();
- }
- }
- private void updateSelectedAudioDevice(@NonNull List<AudioDeviceAttributes> devices) {
- if (devices.isEmpty()) {
- Slog.w(TAG, "The list of preferred devices was empty.");
- return;
- }
-
- AudioDeviceAttributes audioDeviceAttributes = devices.get(0);
-
- if (AudioAttributesUtils.isDeviceOutputAttributes(audioDeviceAttributes)) {
- mDeviceRouteController.selectRoute(
- AudioAttributesUtils.mapToMediaRouteType(audioDeviceAttributes));
- mBluetoothRouteController.selectRoute(null);
- } else if (AudioAttributesUtils.isBluetoothOutputAttributes(audioDeviceAttributes)) {
- mDeviceRouteController.selectRoute(null);
- mBluetoothRouteController.selectRoute(audioDeviceAttributes.getAddress());
- } else {
- Slog.w(TAG, "Unknown audio attributes: " + audioDeviceAttributes);
+ if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
+ if (!TextUtils.equals(selectedDeviceRoute.getId(), route.getId())) {
+ builder.addTransferableRoute(route.getId());
+ }
+ }
+ }
+ return builder.setProviderId(mUniqueId).build();
}
}
@@ -314,7 +286,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
// We must have a device route in the provider info.
- builder.addRoute(mDeviceRouteController.getSelectedRoute());
+ if (Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
+ List<MediaRoute2Info> deviceRoutes = mDeviceRouteController.getAvailableRoutes();
+ for (MediaRoute2Info route : deviceRoutes) {
+ builder.addRoute(route);
+ }
+ setProviderState(builder.build());
+ } else {
+ builder.addRoute(mDeviceRouteController.getSelectedRoute());
+ }
for (MediaRoute2Info route : mBluetoothRouteController.getAllBluetoothRoutes()) {
builder.addRoute(route);
@@ -352,7 +332,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
.setProviderId(mUniqueId)
.build();
builder.addSelectedRoute(mSelectedRouteId);
-
+ for (MediaRoute2Info route : mDeviceRouteController.getAvailableRoutes()) {
+ String routeId = route.getId();
+ if (!mSelectedRouteId.equals(routeId)) {
+ builder.addTransferableRoute(routeId);
+ }
+ }
for (MediaRoute2Info route : mBluetoothRouteController.getTransferableRoutes()) {
builder.addTransferableRoute(route.getId());
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa0b9b892220..3c6887c17e97 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,7 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static android.app.Notification.FLAG_INSISTENT;
+import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_NO_DISMISS;
import static android.app.Notification.FLAG_ONGOING_EVENT;
@@ -58,6 +59,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.Flags.lifetimeExtensionRefactor;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -1224,7 +1226,7 @@ public class NotificationManagerService extends SystemService {
public void onClearAll(int callingUid, int callingPid, int userId) {
synchronized (mNotificationLock) {
cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
- /*includeCurrentProfiles*/ true);
+ /*includeCurrentProfiles*/ true, FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
}
}
@@ -1498,6 +1500,7 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
+ r.recordSmartReplied();
LogMaker logMaker = r.getLogMaker()
.setCategory(MetricsEvent.SMART_REPLY_ACTION)
.setSubtype(replyIndex)
@@ -1804,11 +1807,22 @@ public class NotificationManagerService extends SystemService {
record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
}
if (record != null) {
- cancelNotification(record.getSbn().getUid(), record.getSbn().getInitialPid(),
- record.getSbn().getPackageName(), record.getSbn().getTag(),
- record.getSbn().getId(), 0,
- FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
- true, record.getUserId(), REASON_TIMEOUT, null);
+ if (lifetimeExtensionRefactor()) {
+ cancelNotification(record.getSbn().getUid(),
+ record.getSbn().getInitialPid(),
+ record.getSbn().getPackageName(), record.getSbn().getTag(),
+ record.getSbn().getId(), 0,
+ FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+ | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+ true, record.getUserId(), REASON_TIMEOUT, null);
+ } else {
+ cancelNotification(record.getSbn().getUid(),
+ record.getSbn().getInitialPid(),
+ record.getSbn().getPackageName(), record.getSbn().getTag(),
+ record.getSbn().getId(), 0,
+ FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
+ true, record.getUserId(), REASON_TIMEOUT, null);
+ }
}
}
}
@@ -3728,8 +3742,16 @@ public class NotificationManagerService extends SystemService {
@Override
public void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id,
int userId) {
+ // Don't allow client applications to cancel foreground service notifs, user-initiated
+ // job notifs, autobundled summaries, or notifs that have been replied to.
+ int mustNotHaveFlags = isCallingUidSystem() ? 0 :
+ (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
+ if (lifetimeExtensionRefactor()) {
+ mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ }
+
cancelNotificationInternal(pkg, opPkg, Binder.getCallingUid(), Binder.getCallingPid(),
- tag, id, userId);
+ tag, id, userId, mustNotHaveFlags);
}
@Override
@@ -3740,9 +3762,16 @@ public class NotificationManagerService extends SystemService {
Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
// Don't allow the app to cancel active FGS or UIJ notifications
- cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
- pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
- userId, REASON_APP_CANCEL_ALL);
+ if (lifetimeExtensionRefactor()) {
+ cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
+ pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
+ | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
+ userId, REASON_APP_CANCEL_ALL);
+ } else {
+ cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
+ pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB,
+ userId, REASON_APP_CANCEL_ALL);
+ }
}
@Override
@@ -4808,8 +4837,16 @@ public class NotificationManagerService extends SystemService {
r.getSbn().getId(), userId, reason);
}
} else {
- cancelAllLocked(callingUid, callingPid, info.userid,
- REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles());
+ if (lifetimeExtensionRefactor()) {
+ cancelAllLocked(callingUid, callingPid, info.userid,
+ REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
+ FLAG_ONGOING_EVENT | FLAG_NO_CLEAR
+ | FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ } else {
+ cancelAllLocked(callingUid, callingPid, info.userid,
+ REASON_LISTENER_CANCEL_ALL, info, info.supportsProfiles(),
+ FLAG_ONGOING_EVENT | FLAG_NO_CLEAR);
+ }
}
}
} finally {
@@ -4923,6 +4960,9 @@ public class NotificationManagerService extends SystemService {
int callingUid, int callingPid, String pkg, String tag, int id, int userId,
int reason) {
int mustNotHaveFlags = FLAG_ONGOING_EVENT;
+ if (lifetimeExtensionRefactor()) {
+ mustNotHaveFlags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ }
cancelNotification(callingUid, callingPid, pkg, tag, id, 0 /* mustHaveFlags */,
mustNotHaveFlags,
true,
@@ -6712,7 +6752,12 @@ public class NotificationManagerService extends SystemService {
@Override
public void cancelNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, int userId) {
- cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId);
+ // Don't allow client applications to cancel foreground service notifs,
+ // user-initiated job notifs or autobundled summaries.
+ final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
+ (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
+ cancelNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, userId,
+ mustNotHaveFlags);
}
@Override
@@ -6907,7 +6952,7 @@ public class NotificationManagerService extends SystemService {
}
void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
- String tag, int id, int userId) {
+ String tag, int id, int userId, int mustNotHaveFlags) {
userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
@@ -6935,10 +6980,6 @@ public class NotificationManagerService extends SystemService {
}
}
- // Don't allow client applications to cancel foreground service notifs, user-initiated job
- // notifs or autobundled summaries.
- final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
- (FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB | FLAG_AUTOGROUP_SUMMARY);
cancelNotification(uid, callingPid, pkg, tag, id, 0,
mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
}
@@ -7294,6 +7335,11 @@ public class NotificationManagerService extends SystemService {
notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
+ // Apps should not create notifications that are lifetime extended.
+ if (lifetimeExtensionRefactor()) {
+ notification.flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ }
+
if (notification.fullScreenIntent != null) {
final AttributionSource attributionSource =
new AttributionSource.Builder(notificationUid).setPackageName(pkg).build();
@@ -10088,7 +10134,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
- ManagedServiceInfo listener, boolean includeCurrentProfiles) {
+ ManagedServiceInfo listener, boolean includeCurrentProfiles, int mustNotHaveFlags) {
final long cancellationElapsedTimeMs = SystemClock.elapsedRealtime();
mHandler.post(new Runnable() {
@Override
@@ -10100,7 +10146,7 @@ public class NotificationManagerService extends SystemService {
null, userId, 0, 0, reason, listenerName);
FlagChecker flagChecker = (int flags) -> {
- int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+ int flagsToCheck = mustNotHaveFlags;
if (REASON_LISTENER_CANCEL_ALL == reason
|| REASON_CANCEL_ALL == reason) {
flagsToCheck |= FLAG_BUBBLE;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 100c63863f7f..64d3a20e9281 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -24,7 +24,9 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
+import android.app.Flags;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -1257,10 +1259,27 @@ public final class NotificationRecord {
mStats.setExpanded();
}
+ /** Run when the notification is direct replied. */
public void recordDirectReplied() {
+ if (Flags.lifetimeExtensionRefactor()) {
+ // Mark the NotificationRecord as lifetime extended.
+ Notification notification = getSbn().getNotification();
+ notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ }
+
mStats.setDirectReplied();
}
+
+ /** Run when the notification is smart replied. */
+ @FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void recordSmartReplied() {
+ Notification notification = getSbn().getNotification();
+ notification.flags |= Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+
+ mStats.setSmartReplied();
+ }
+
public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
mStats.setDismissalSurface(surface);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3b3d79e7dee1..07e0ddfac76b 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -357,6 +357,12 @@ final class DeletePackageHelper {
final DeletePackageAction action;
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Attempted to remove non-existent package " + packageName);
+ }
+ return false;
+ }
final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
if (PackageManagerServiceUtils.isSystemApp(ps)
&& mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 0a81b2b9fabb..5494bd9808c8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -539,6 +539,12 @@ final class InstallRequest {
}
@Nullable
+ public PackageSetting getScanRequestDisabledPackageSetting() {
+ assertScanResultExists();
+ return mScanResult.mRequest.mDisabledPkgSetting;
+ }
+
+ @Nullable
public String getRealPackageName() {
assertScanResultExists();
return mScanResult.mRequest.mRealPkgName;
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index d0fdfa9bc775..9384c13e583b 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -856,9 +856,9 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? installRequest.getDisabledPackageSetting() == null
+ ? installRequest.getScanRequestDisabledPackageSetting() == null
? installRequest.getScanRequestOldPackageSetting()
- : installRequest.getDisabledPackageSetting()
+ : installRequest.getScanRequestDisabledPackageSetting()
: null;
if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
|| updatedSystemPs.getPkg().getLibraryNames() == null)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f90bf4b47644..b89b4a2cb241 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1566,6 +1566,7 @@ public class UserManagerService extends IUserManager.Stub {
: now - userData.info.creationTime);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.REQUEST_QUIET_MODE_ENABLED)
+ .setInt(UserJourneyLogger.getUserTypeForStatsd(userData.info.userType))
.setStrings(callingPackage)
.setBoolean(enableQuietMode)
.setTimePeriod(period)
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6f6bb45bad50..f7f76aaaee16 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -807,7 +807,7 @@ final class DefaultPermissionGrantPolicy {
getDefaultSystemHandlerActivityPackage(pm,
SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- NOTIFICATION_PERMISSIONS);
+ NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS);
}
// Voice recognition
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 73c422490330..e817df8c6e0c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3638,15 +3638,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
break;
- case KeyEvent.KEYCODE_SPACE:
- // Handle keyboard layout switching. (META + SPACE)
- if (firstDown && event.isMetaPressed()) {
- int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- sendSwitchKeyboardLayout(event, direction);
- logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
- return true;
- }
- break;
case KeyEvent.KEYCODE_META_LEFT:
case KeyEvent.KEYCODE_META_RIGHT:
if (down) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index e3f36385ef6c..5d9085144334 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -32,7 +32,6 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
@@ -41,6 +40,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ConcurrentUtils;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -809,12 +809,13 @@ public class BatterySaverPolicy extends ContentObserver implements
private static Policy fromSettings(String settings, String deviceSpecificSettings,
DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
- final KeyValueListParser parser = new KeyValueListParser(',');
+ final UserSettingDeviceConfigMediator userSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
configSuffix = TextUtils.emptyIfNull(configSuffix);
// Device-specific parameters.
try {
- parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
+ userSettingDeviceConfigMediator.setSettingsString(deviceSpecificSettings);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad device specific battery saver constants: "
+ deviceSpecificSettings);
@@ -822,68 +823,58 @@ public class BatterySaverPolicy extends ContentObserver implements
// Non-device-specific parameters.
try {
- parser.setString(settings == null ? "" : settings);
+ userSettingDeviceConfigMediator.setSettingsString(settings);
+ userSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad battery saver constants: " + settings);
}
// The Settings value overrides everything, since that will be set by the user.
// The DeviceConfig value takes second place, with the default as the last choice.
- final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
- properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
- defaultPolicy.adjustBrightnessFactor));
- final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
- properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
- defaultPolicy.advertiseIsEnabled));
- final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
- properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
- defaultPolicy.deferFullBackup));
- final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
- properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
- defaultPolicy.deferKeyValueBackup));
- final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
- properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
- defaultPolicy.disableAnimation));
- final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
- properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
- defaultPolicy.disableAod));
- final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
- properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
- defaultPolicy.disableLaunchBoost));
- final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
- properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
- defaultPolicy.disableOptionalSensors));
- final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
- properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
- defaultPolicy.disableVibration));
- final boolean enableBrightnessAdjustment = parser.getBoolean(
- KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
- properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
- defaultPolicy.enableAdjustBrightness));
- final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
- properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
- defaultPolicy.enableDataSaver));
- final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
- properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
- defaultPolicy.enableFirewall));
- final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
- properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
- defaultPolicy.enableNightMode));
- final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
- properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
- defaultPolicy.enableQuickDoze));
- final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
- properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
- defaultPolicy.forceAllAppsStandby));
- final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
- properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
- defaultPolicy.forceBackgroundCheck));
- final int locationMode = parser.getInt(KEY_LOCATION_MODE,
- properties.getInt(KEY_LOCATION_MODE + configSuffix,
- defaultPolicy.locationMode));
- final int soundTriggerMode = parser.getInt(KEY_SOUNDTRIGGER_MODE,
- properties.getInt(KEY_SOUNDTRIGGER_MODE + configSuffix,
- defaultPolicy.soundTriggerMode));
+ final float adjustBrightnessFactor = userSettingDeviceConfigMediator.getFloat(
+ KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
+ defaultPolicy.adjustBrightnessFactor);
+ final boolean advertiseIsEnabled = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ADVERTISE_IS_ENABLED + configSuffix,
+ defaultPolicy.advertiseIsEnabled);
+ final boolean deferFullBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_FULL_BACKUP + configSuffix, defaultPolicy.deferFullBackup);
+ final boolean deferKeyValueBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
+ defaultPolicy.deferKeyValueBackup);
+ final boolean disableAnimation = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_ANIMATION + configSuffix, defaultPolicy.disableAnimation);
+ final boolean disableAod = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_AOD + configSuffix, defaultPolicy.disableAod);
+ final boolean disableLaunchBoost = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_LAUNCH_BOOST + configSuffix,
+ defaultPolicy.disableLaunchBoost);
+ final boolean disableOptionalSensors = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
+ defaultPolicy.disableOptionalSensors);
+ final boolean disableVibrationConfig = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_VIBRATION + configSuffix, defaultPolicy.disableVibration);
+ final boolean enableBrightnessAdjustment = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
+ defaultPolicy.enableAdjustBrightness);
+ final boolean enableDataSaver = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_DATASAVER + configSuffix, defaultPolicy.enableDataSaver);
+ final boolean enableFirewall = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_FIREWALL + configSuffix, defaultPolicy.enableFirewall);
+ final boolean enableNightMode = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_NIGHT_MODE + configSuffix, defaultPolicy.enableNightMode);
+ final boolean enableQuickDoze = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_QUICK_DOZE + configSuffix, defaultPolicy.enableQuickDoze);
+ final boolean forceAllAppsStandby = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
+ defaultPolicy.forceAllAppsStandby);
+ final boolean forceBackgroundCheck = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_BACKGROUND_CHECK + configSuffix,
+ defaultPolicy.forceBackgroundCheck);
+ final int locationMode = userSettingDeviceConfigMediator.getInt(
+ KEY_LOCATION_MODE + configSuffix, defaultPolicy.locationMode);
+ final int soundTriggerMode = userSettingDeviceConfigMediator.getInt(
+ KEY_SOUNDTRIGGER_MODE + configSuffix, defaultPolicy.soundTriggerMode);
return new Policy(
adjustBrightnessFactor,
advertiseIsEnabled,
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index dd39fb02573e..c17d6ab4c85b 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -51,6 +51,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.Objects;
/** An hint service implementation that runs in System Server process. */
@@ -544,7 +545,11 @@ public final class HintManagerService extends SystemService {
if (mHalSessionPtr == 0) return;
mNativeWrapper.halCloseHintSession(mHalSessionPtr);
mHalSessionPtr = 0;
- mToken.unlinkToDeath(this, 0);
+ try {
+ mToken.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException ignored) {
+ Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
+ }
}
synchronized (mLock) {
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index aadd03b25428..894226cf32c9 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -33,6 +33,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -265,4 +266,11 @@ class AggregatedPowerStats {
ipw.decreaseIndent();
}
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dump(new IndentingPrintWriter(sw));
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index f9d57e4c9042..a8eda3ca6a47 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -44,11 +44,8 @@ import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.power.EnergyConsumerStats;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
-import libcore.util.EmptyArray;
-
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -128,9 +125,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
private boolean mUseLatestStates = true;
@GuardedBy("this")
- private final IntArray mUidsToRemove = new IntArray();
-
- @GuardedBy("this")
private Future<?> mWakelockChangesUpdate;
@GuardedBy("this")
@@ -260,7 +254,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
@Override
public synchronized Future<?> scheduleCpuSyncDueToRemovedUid(int uid) {
- mUidsToRemove.add(uid);
return scheduleSyncLocked("remove-uid", UPDATE_CPU);
}
@@ -459,7 +452,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
// Capture a snapshot of the state we are meant to process.
final int updateFlags;
final String reason;
- final int[] uidsToRemove;
final boolean onBattery;
final boolean onBatteryScreenOff;
final int screenState;
@@ -468,7 +460,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
synchronized (BatteryExternalStatsWorker.this) {
updateFlags = mUpdateFlags;
reason = mCurrentReason;
- uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT;
onBattery = mOnBattery;
onBatteryScreenOff = mOnBatteryScreenOff;
screenState = mScreenState;
@@ -476,7 +467,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
useLatestStates = mUseLatestStates;
mUpdateFlags = 0;
mCurrentReason = null;
- mUidsToRemove.clear();
mCurrentFuture = null;
mUseLatestStates = true;
if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
@@ -512,12 +502,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
// Clean up any UIDs if necessary.
synchronized (mStats) {
- for (int uid : uidsToRemove) {
- FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, uid,
- FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
- mStats.maybeRemoveIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
- SystemClock.uptimeMillis());
- }
mStats.clearPendingRemovedUidsLocked();
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java
new file mode 100644
index 000000000000..ad146afe16e7
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+
+public class BatteryStatsDumpHelperImpl implements BatteryStats.BatteryStatsDumpHelper {
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+
+ public BatteryStatsDumpHelperImpl(BatteryUsageStatsProvider batteryUsageStatsProvider) {
+ mBatteryUsageStatsProvider = batteryUsageStatsProvider;
+ }
+
+ @Override
+ public BatteryUsageStats getBatteryUsageStats(BatteryStats batteryStats, boolean detailed) {
+ BatteryUsageStatsQuery.Builder builder = new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0);
+ if (detailed) {
+ builder.includePowerModels().includeProcessStateData().includeVirtualUids();
+ }
+ return mBatteryUsageStatsProvider.getBatteryUsageStats((BatteryStatsImpl) batteryStats,
+ builder.build());
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index eb401043af03..0491c14e52bd 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -185,7 +185,7 @@ public class BatteryStatsImpl extends BatteryStats {
// TODO: remove "tcp" from network methods, since we measure total stats.
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- public static final int VERSION = 213;
+ public static final int VERSION = 214;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -220,6 +220,8 @@ public class BatteryStatsImpl extends BatteryStats {
public static final int RESET_REASON_FULL_CHARGE = 3;
public static final int RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE = 4;
public static final int RESET_REASON_PLUGGED_IN_FOR_LONG_DURATION = 5;
+ @NonNull
+ private final MonotonicClock mMonotonicClock;
protected Clock mClock;
@@ -393,19 +395,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- /**
- * Listener for the battery stats reset.
- */
- public interface BatteryResetListener {
-
- /**
- * Callback invoked immediately prior to resetting battery stats.
- * @param resetReason One of the RESET_REASON_* constants.
- */
- void prepareForBatteryStatsReset(int resetReason);
- }
-
- private BatteryResetListener mBatteryResetListener;
+ private boolean mSaveBatteryUsageStatsOnReset;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private PowerStatsStore mPowerStatsStore;
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
@@ -787,13 +779,10 @@ public class BatteryStatsImpl extends BatteryStats {
private BatteryCallback mCallback;
/**
- * Mapping isolated uids to the actual owning app uid.
- */
- private final SparseIntArray mIsolatedUids = new SparseIntArray();
- /**
- * Internal reference count of isolated uids.
+ * Mapping child uids to their parent uid.
*/
- private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+ @VisibleForTesting
+ protected final PowerStatsUidResolver mPowerStatsUidResolver;
/**
* The statistics we have collected organized by uids.
@@ -874,6 +863,8 @@ public class BatteryStatsImpl extends BatteryStats {
long mUptimeStartUs;
long mRealtimeUs;
long mRealtimeStartUs;
+ long mMonotonicStartTime;
+ long mMonotonicEndTime = MonotonicClock.UNDEFINED;
int mWakeLockNesting;
boolean mWakeLockImportant;
@@ -1724,25 +1715,26 @@ public class BatteryStatsImpl extends BatteryStats {
}
@VisibleForTesting
- public BatteryStatsImpl(Clock clock, File historyDirectory) {
+ public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver) {
init(clock);
mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
- mHandler = null;
+ mHandler = handler;
+ mPowerStatsUidResolver = powerStatsUidResolver;
mConstants = new Constants(mHandler);
mStartClockTimeMs = clock.currentTimeMillis();
mDailyFile = null;
+ mMonotonicClock = new MonotonicClock(0, mClock);
if (historyDirectory == null) {
mCheckinFile = null;
mStatsFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
- new MonotonicClock(0, mClock));
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
} else {
mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
- new MonotonicClock(0, mClock));
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
}
mPlatformIdleStateCallback = null;
mEnergyConsumerRetriever = null;
@@ -4278,92 +4270,51 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- @GuardedBy("this")
- public void addIsolatedUidLocked(int isolatedUid, int appUid) {
- addIsolatedUidLocked(isolatedUid, appUid,
- mClock.elapsedRealtime(), mClock.uptimeMillis());
+ private void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ long uptime = mClock.uptimeMillis();
+ synchronized (this) {
+ getUidStatsLocked(parentUid, realtime, uptime).addIsolatedUid(isolatedUid);
+ }
}
- @GuardedBy("this")
- @SuppressWarnings("GuardedBy") // errorprone false positive on u.addIsolatedUid
- public void addIsolatedUidLocked(int isolatedUid, int appUid,
- long elapsedRealtimeMs, long uptimeMs) {
- mIsolatedUids.put(isolatedUid, appUid);
- mIsolatedUidRefCounts.put(isolatedUid, 1);
- final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
- u.addIsolatedUid(isolatedUid);
+ private void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ mPowerStatsUidResolver.retainIsolatedUid(isolatedUid);
+ synchronized (this) {
+ mPendingRemovedUids.add(new UidToRemove(isolatedUid, realtime));
+ }
+ if (mExternalSync != null) {
+ mExternalSync.scheduleCpuSyncDueToRemovedUid(isolatedUid);
+ }
}
- /**
- * Schedules a read of the latest cpu times before removing the isolated UID.
- * @see #removeIsolatedUidLocked(int, int, int)
- */
- public void scheduleRemoveIsolatedUidLocked(int isolatedUid, int appUid) {
- int curUid = mIsolatedUids.get(isolatedUid, -1);
- if (curUid == appUid) {
- if (mExternalSync != null) {
- mExternalSync.scheduleCpuSyncDueToRemovedUid(isolatedUid);
- }
+ private void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ long uptime = mClock.uptimeMillis();
+ synchronized (this) {
+ getUidStatsLocked(parentUid, realtime, uptime).removeIsolatedUid(isolatedUid);
}
}
/**
* Isolated uid should only be removed after all wakelocks associated with the uid are stopped
* and the cpu time-in-state has been read one last time for the uid.
- *
- * @see #scheduleRemoveIsolatedUidLocked(int, int)
- *
- * @return true if the isolated uid is actually removed.
*/
@GuardedBy("this")
- public boolean maybeRemoveIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs,
- long uptimeMs) {
- final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
- if (refCount > 0) {
- // Isolated uid is still being tracked
- mIsolatedUidRefCounts.put(isolatedUid, refCount);
- return false;
- }
-
- final int idx = mIsolatedUids.indexOfKey(isolatedUid);
- if (idx >= 0) {
- final int ownerUid = mIsolatedUids.valueAt(idx);
- final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
- u.removeIsolatedUid(isolatedUid);
- mIsolatedUids.removeAt(idx);
- mIsolatedUidRefCounts.delete(isolatedUid);
- } else {
- Slog.w(TAG, "Attempted to remove untracked isolated uid (" + isolatedUid + ")");
- }
- mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
-
- return true;
- }
-
- /**
- * Increment the ref count for an isolated uid.
- * call #maybeRemoveIsolatedUidLocked to decrement.
- */
- public void incrementIsolatedUidRefCount(int uid) {
- final int refCount = mIsolatedUidRefCounts.get(uid, 0);
- if (refCount <= 0) {
- // Uid is not mapped or referenced
- Slog.w(TAG,
- "Attempted to increment ref counted of untracked isolated uid (" + uid + ")");
- return;
- }
- mIsolatedUidRefCounts.put(uid, refCount + 1);
+ public void releaseIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs, long uptimeMs) {
+ mPowerStatsUidResolver.releaseIsolatedUid(isolatedUid);
}
private int mapUid(int uid) {
if (Process.isSdkSandboxUid(uid)) {
return Process.getAppUidForSdkSandboxUid(uid);
}
- return mapIsolatedUid(uid);
+ return mPowerStatsUidResolver.mapUid(uid);
}
private int mapIsolatedUid(int uid) {
- return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid);
+ return mPowerStatsUidResolver.mapUid(uid);
}
@GuardedBy("this")
@@ -4745,7 +4696,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
- incrementIsolatedUidRefCount(uid);
+ mPowerStatsUidResolver.retainIsolatedUid(uid);
}
if (mOnBatteryScreenOffTimeBase.isRunning()) {
// We only update the cpu time when a wake lock is acquired if the screen is off.
@@ -4825,7 +4776,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
- maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+ releaseIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@@ -4996,7 +4947,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
- incrementIsolatedUidRefCount(uid);
+ mPowerStatsUidResolver.retainIsolatedUid(uid);
}
}
@@ -5048,7 +4999,7 @@ public class BatteryStatsImpl extends BatteryStats {
historyName, mappedUid);
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
- maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+ releaseIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
@@ -7642,35 +7593,53 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Returns the names of custom power components.
*/
- @GuardedBy("this")
@Override
public @NonNull String[] getCustomEnergyConsumerNames() {
- if (mEnergyConsumerStatsConfig == null) {
- return new String[0];
- }
- final String[] names = mEnergyConsumerStatsConfig.getCustomBucketNames();
- for (int i = 0; i < names.length; i++) {
- if (TextUtils.isEmpty(names[i])) {
- names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
+ synchronized (this) {
+ if (mEnergyConsumerStatsConfig == null) {
+ return new String[0];
}
+ final String[] names = mEnergyConsumerStatsConfig.getCustomBucketNames();
+ for (int i = 0; i < names.length; i++) {
+ if (TextUtils.isEmpty(names[i])) {
+ names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
+ }
+ }
+ return names;
}
- return names;
}
- @GuardedBy("this")
- @Override public long getStartClockTime() {
- final long currentTimeMs = mClock.currentTimeMillis();
- if ((currentTimeMs > MILLISECONDS_IN_YEAR
- && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
+ @Override
+ public long getStartClockTime() {
+ synchronized (this) {
+ final long currentTimeMs = mClock.currentTimeMillis();
+ if ((currentTimeMs > MILLISECONDS_IN_YEAR
+ && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
|| (mStartClockTimeMs > currentTimeMs)) {
- // If the start clock time has changed by more than a year, then presumably
- // the previous time was completely bogus. So we are going to figure out a
- // new time based on how much time has elapsed since we started counting.
- mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
- currentTimeMs);
- adjustStartClockTime(currentTimeMs);
+ // If the start clock time has changed by more than a year, then presumably
+ // the previous time was completely bogus. So we are going to figure out a
+ // new time based on how much time has elapsed since we started counting.
+ mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ currentTimeMs);
+ adjustStartClockTime(currentTimeMs);
+ }
+ return mStartClockTimeMs;
}
- return mStartClockTimeMs;
+ }
+
+ /**
+ * Returns the monotonic time when the BatteryStats session started.
+ */
+ public long getMonotonicStartTime() {
+ return mMonotonicStartTime;
+ }
+
+ /**
+ * Returns the monotonic time when the BatteryStats session ended, or
+ * {@link MonotonicClock#UNDEFINED} if the session is still ongoing.
+ */
+ public long getMonotonicEndTime() {
+ return mMonotonicEndTime;
}
@Override public String getStartPlatformVersion() {
@@ -8197,7 +8166,9 @@ public class BatteryStatsImpl extends BatteryStats {
return mProportionalSystemServiceUsage;
}
- @GuardedBy("mBsi")
+ /**
+ * Adds isolated UID to the list of children.
+ */
public void addIsolatedUid(int isolatedUid) {
if (mChildUids == null) {
mChildUids = new SparseArray<>();
@@ -8207,6 +8178,9 @@ public class BatteryStatsImpl extends BatteryStats {
mChildUids.put(isolatedUid, new ChildUid());
}
+ /**
+ * Removes isolated UID from the list of children.
+ */
public void removeIsolatedUid(int isolatedUid) {
final int idx = mChildUids == null ? -1 : mChildUids.indexOfKey(isolatedUid);
if (idx < 0) {
@@ -10910,15 +10884,18 @@ public class BatteryStatsImpl extends BatteryStats {
@NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
@Nullable EnergyStatsRetriever energyStatsCb,
@NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
- @NonNull CpuScalingPolicies cpuScalingPolicies) {
+ @NonNull CpuScalingPolicies cpuScalingPolicies,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver) {
init(clock);
mBatteryStatsConfig = config;
+ mMonotonicClock = monotonicClock;
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
mPowerProfile = powerProfile;
mCpuScalingPolicies = cpuScalingPolicies;
+ mPowerStatsUidResolver = powerStatsUidResolver;
initPowerProfile();
@@ -10927,17 +10904,17 @@ public class BatteryStatsImpl extends BatteryStats {
mCheckinFile = null;
mDailyFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
}
mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
- () -> mBatteryVoltageMv, mHandler,
+ mPowerStatsUidResolver, () -> mBatteryVoltageMv, mHandler,
mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
@@ -10954,6 +10931,23 @@ public class BatteryStatsImpl extends BatteryStats {
mEnergyConsumerRetriever = energyStatsCb;
mUserInfoProvider = userInfoProvider;
+ mPowerStatsUidResolver.addListener(new PowerStatsUidResolver.Listener() {
+ @Override
+ public void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onIsolatedUidAdded(isolatedUid, parentUid);
+ }
+
+ @Override
+ public void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onBeforeIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+
+ @Override
+ public void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onAfterIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+ });
+
// Notify statsd that the system is initially not in doze.
mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
@@ -11497,6 +11491,7 @@ public class BatteryStatsImpl extends BatteryStats {
mUptimeUs = 0;
mRealtimeStartUs = realtimeUs;
mUptimeStartUs = uptimeUs;
+ mMonotonicStartTime = mMonotonicClock.monotonicTime();
}
void initDischarge(long elapsedRealtimeUs) {
@@ -11517,8 +11512,17 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
- public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
- mBatteryResetListener = batteryResetListener;
+ /**
+ * Associates the BatteryStatsImpl object with a BatteryUsageStatsProvider and PowerStatsStore
+ * to allow for a snapshot of battery usage stats to be taken and stored just before battery
+ * reset.
+ */
+ public void saveBatteryUsageStatsOnReset(
+ @NonNull BatteryUsageStatsProvider batteryUsageStatsProvider,
+ @NonNull PowerStatsStore powerStatsStore) {
+ mSaveBatteryUsageStatsOnReset = true;
+ mBatteryUsageStatsProvider = batteryUsageStatsProvider;
+ mPowerStatsStore = powerStatsStore;
}
@GuardedBy("this")
@@ -11557,9 +11561,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis,
int resetReason) {
- if (mBatteryResetListener != null) {
- mBatteryResetListener.prepareForBatteryStatsReset(resetReason);
- }
+ saveBatteryUsageStatsOnReset(resetReason);
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
@@ -11707,6 +11709,31 @@ public class BatteryStatsImpl extends BatteryStats {
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
+ private void saveBatteryUsageStatsOnReset(int resetReason) {
+ if (!mSaveBatteryUsageStatsOnReset
+ || resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
+ return;
+ }
+
+ final BatteryUsageStats batteryUsageStats;
+ synchronized (this) {
+ batteryUsageStats = mBatteryUsageStatsProvider.getBatteryUsageStats(this,
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .includeProcessStateData()
+ .build());
+ }
+
+ // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
+ // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
+ // start time
+ long monotonicStartTime =
+ mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
+ mHandler.post(() ->
+ mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
+ }
+
@GuardedBy("this")
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
@@ -15137,6 +15164,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
}
+ mPowerStatsUidResolver.releaseUidsInRange(startUid, endUid);
// Treat as one. We don't know how many uids there are in between.
mNumUidsRemoved++;
} else {
@@ -15192,10 +15220,11 @@ public class BatteryStatsImpl extends BatteryStats {
mShuttingDown = true;
}
- @GuardedBy("this")
@Override
public boolean isProcessStateDataAvailable() {
- return trackPerProcStateCpuTimes();
+ synchronized (this) {
+ return trackPerProcStateCpuTimes();
+ }
}
@GuardedBy("this")
@@ -15862,6 +15891,8 @@ public class BatteryStatsImpl extends BatteryStats {
mUptimeUs = in.readLong();
mRealtimeUs = in.readLong();
mStartClockTimeMs = in.readLong();
+ mMonotonicStartTime = in.readLong();
+ mMonotonicEndTime = in.readLong();
mStartPlatformVersion = in.readString();
mEndPlatformVersion = in.readString();
mOnBatteryTimeBase.readSummaryFromParcel(in);
@@ -16382,6 +16413,8 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(nowRealtime, STATS_SINCE_CHARGED));
out.writeLong(mStartClockTimeMs);
+ out.writeLong(mMonotonicStartTime);
+ out.writeLong(mMonotonicClock.monotonicTime());
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
mOnBatteryTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
@@ -16912,7 +16945,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart,
+ BatteryStatsDumpHelper dumpHelper) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
mOnBatteryTimeBase.dump(pw, " ");
@@ -16984,7 +17018,7 @@ public class BatteryStatsImpl extends BatteryStats {
pr.println("*** Camera timer:");
mCameraOnTimer.logState(pr, " ");
}
- super.dump(context, pw, flags, reqUid, histStart);
+ super.dump(context, pw, flags, reqUid, histStart, dumpHelper);
synchronized (this) {
pw.print("Per process state tracking available: ");
@@ -16998,15 +17032,7 @@ public class BatteryStatsImpl extends BatteryStats {
pw.print("UIDs removed since the later of device start or stats reset: ");
pw.println(mNumUidsRemoved);
- pw.println("Currently mapped isolated uids:");
- final int numIsolatedUids = mIsolatedUids.size();
- for (int i = 0; i < numIsolatedUids; i++) {
- final int isolatedUid = mIsolatedUids.keyAt(i);
- final int ownerUid = mIsolatedUids.valueAt(i);
- final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
- pw.println(
- " " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
- }
+ mPowerStatsUidResolver.dump(pw);
pw.println();
dumpConstantsLocked(pw);
@@ -17020,15 +17046,4 @@ public class BatteryStatsImpl extends BatteryStats {
dumpEnergyConsumerStatsLocked(pw);
}
}
-
- @Override
- protected BatteryUsageStats getBatteryUsageStats(Context context, boolean detailed) {
- final BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, this);
- BatteryUsageStatsQuery.Builder builder = new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0);
- if (detailed) {
- builder.includePowerModels().includeProcessStateData().includeVirtualUids();
- }
- return provider.getBatteryUsageStats(builder.build());
- }
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 83d7d72059b4..303c2457165a 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -23,14 +23,12 @@ import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
@@ -45,27 +43,25 @@ import java.util.List;
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
- private final BatteryStats mStats;
+ private boolean mPowerStatsExporterEnabled;
+ private final PowerStatsExporter mPowerStatsExporter;
private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
+ private final Clock mClock;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
- public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
- this(context, stats, null);
- }
-
- @VisibleForTesting
- public BatteryUsageStatsProvider(Context context, BatteryStats stats,
- PowerStatsStore powerStatsStore) {
+ public BatteryUsageStatsProvider(Context context,
+ PowerStatsExporter powerStatsExporter,
+ PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
+ PowerStatsStore powerStatsStore, Clock clock) {
mContext = context;
- mStats = stats;
+ mPowerStatsExporter = powerStatsExporter;
mPowerStatsStore = powerStatsStore;
- mPowerProfile = stats instanceof BatteryStatsImpl
- ? ((BatteryStatsImpl) stats).getPowerProfile()
- : new PowerProfile(context);
- mCpuScalingPolicies = stats.getCpuScalingPolicies();
+ mPowerProfile = powerProfile;
+ mCpuScalingPolicies = cpuScalingPolicies;
+ mClock = clock;
}
private List<PowerCalculator> getPowerCalculators() {
@@ -75,7 +71,10 @@ public class BatteryUsageStatsProvider {
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- mPowerCalculators.add(new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ if (mPowerStatsExporterEnabled) {
+ mPowerCalculators.add(
+ new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ }
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
@@ -111,27 +110,28 @@ public class BatteryUsageStatsProvider {
* Returns true if the last update was too long ago for the tolerances specified
* by the supplied queries.
*/
- public boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
- long lastUpdateTimeStampMs) {
+ public static boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
+ long elapsedRealtime, long lastUpdateTimeStampMs) {
long allowableStatsAge = Long.MAX_VALUE;
for (int i = queries.size() - 1; i >= 0; i--) {
BatteryUsageStatsQuery query = queries.get(i);
allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
}
- return elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
+ return elapsedRealtime - lastUpdateTimeStampMs > allowableStatsAge;
}
/**
* Returns snapshots of battery attribution data, one per supplied query.
*/
- public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
+ public List<BatteryUsageStats> getBatteryUsageStats(BatteryStatsImpl stats,
+ List<BatteryUsageStatsQuery> queries) {
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
- synchronized (mStats) {
- mStats.prepareForDumpLocked();
- final long currentTimeMillis = currentTimeMillis();
+ synchronized (stats) {
+ stats.prepareForDumpLocked();
+ final long currentTimeMillis = mClock.currentTimeMillis();
for (int i = 0; i < queries.size(); i++) {
- results.add(getBatteryUsageStats(queries.get(i), currentTimeMillis));
+ results.add(getBatteryUsageStats(stats, queries.get(i), currentTimeMillis));
}
}
return results;
@@ -140,60 +140,59 @@ public class BatteryUsageStatsProvider {
/**
* Returns a snapshot of battery attribution data.
*/
- @VisibleForTesting
- public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
- synchronized (mStats) {
- return getBatteryUsageStats(query, currentTimeMillis());
- }
+ public BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query) {
+ return getBatteryUsageStats(stats, query, mClock.currentTimeMillis());
}
- @GuardedBy("mStats")
- private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
- long currentTimeMs) {
+ private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query, long currentTimeMs) {
if (query.getToTimestamp() == 0) {
- return getCurrentBatteryUsageStats(query, currentTimeMs);
+ return getCurrentBatteryUsageStats(stats, query, currentTimeMs);
} else {
- return getAggregatedBatteryUsageStats(query);
+ return getAggregatedBatteryUsageStats(stats, query);
}
}
- @GuardedBy("mStats")
- private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
- long currentTimeMs) {
- final long realtimeUs = elapsedRealtime() * 1000;
- final long uptimeUs = uptimeMillis() * 1000;
+ private BatteryUsageStats getCurrentBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query, long currentTimeMs) {
+ final long realtimeUs = mClock.elapsedRealtime() * 1000;
+ final long uptimeUs = mClock.uptimeMillis() * 1000;
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
- && mStats.isProcessStateDataAvailable();
+ && stats.isProcessStateDataAvailable();
final boolean includeVirtualUids = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
- mStats.getCustomEnergyConsumerNames(), includePowerModels,
+ stats.getCustomEnergyConsumerNames(), includePowerModels,
includeProcessStateData, minConsumedPowerThreshold);
// TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
- // of stats sessions to wall-clock adjustments
- batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
+ // of batteryUsageStats sessions to wall-clock adjustments
+ batteryUsageStatsBuilder.setStatsStartTimestamp(stats.getStartClockTime());
batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);
- SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
- for (int i = uidStats.size() - 1; i >= 0; i--) {
- final BatteryStats.Uid uid = uidStats.valueAt(i);
- if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
- continue;
- }
+ synchronized (stats) {
+ SparseArray<? extends BatteryStats.Uid> uidStats = stats.getUidStats();
+ for (int i = uidStats.size() - 1; i >= 0; i--) {
+ final BatteryStats.Uid uid = uidStats.valueAt(i);
+ if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
+ continue;
+ }
- batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
- getProcessBackgroundTimeMs(uid, realtimeUs))
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
- getProcessForegroundTimeMs(uid, realtimeUs))
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
- getProcessForegroundServiceTimeMs(uid, realtimeUs));
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
+ getProcessBackgroundTimeMs(uid, realtimeUs))
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
+ getProcessForegroundTimeMs(uid, realtimeUs))
+ .setTimeInProcessStateMs(
+ UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
+ getProcessForegroundServiceTimeMs(uid, realtimeUs));
+ }
}
final int[] powerComponents = query.getPowerComponents();
@@ -202,8 +201,8 @@ public class BatteryUsageStatsProvider {
PowerCalculator powerCalculator = powerCalculators.get(i);
if (powerComponents != null) {
boolean include = false;
- for (int j = 0; j < powerComponents.length; j++) {
- if (powerCalculator.isPowerComponentSupported(powerComponents[j])) {
+ for (int powerComponent : powerComponents) {
+ if (powerCalculator.isPowerComponentSupported(powerComponent)) {
include = true;
break;
}
@@ -212,26 +211,24 @@ public class BatteryUsageStatsProvider {
continue;
}
}
- powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
- query);
+ powerCalculator.calculate(batteryUsageStatsBuilder, stats, realtimeUs, uptimeUs, query);
+ }
+
+ if (mPowerStatsExporterEnabled) {
+ mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
+ stats.getMonotonicStartTime(), stats.getMonotonicEndTime());
}
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
- if (!(mStats instanceof BatteryStatsImpl)) {
- throw new UnsupportedOperationException(
- "History cannot be included for " + getClass().getName());
- }
-
- BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.copyHistory());
+ batteryUsageStatsBuilder.setBatteryHistory(stats.copyHistory());
}
- BatteryUsageStats stats = batteryUsageStatsBuilder.build();
+ BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
if (includeProcessStateData) {
- verify(stats);
+ verify(batteryUsageStats);
}
- return stats;
+ return batteryUsageStats;
}
// STOPSHIP(b/229906525): remove verification before shipping
@@ -308,15 +305,16 @@ public class BatteryUsageStatsProvider {
/ 1000;
}
- private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
+ private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query) {
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
- && mStats.isProcessStateDataAvailable();
+ && stats.isProcessStateDataAvailable();
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
- final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
+ final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
customEnergyConsumerNames, includePowerModels, includeProcessStateData,
minConsumedPowerThreshold);
@@ -386,27 +384,8 @@ public class BatteryUsageStatsProvider {
return builder.build();
}
- private long elapsedRealtime() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.elapsedRealtime();
- } else {
- return SystemClock.elapsedRealtime();
- }
- }
+ public void setPowerStatsExporterEnabled(boolean enabled) {
- private long uptimeMillis() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.uptimeMillis();
- } else {
- return SystemClock.uptimeMillis();
- }
- }
-
- private long currentTimeMillis() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.currentTimeMillis();
- } else {
- return System.currentTimeMillis();
- }
+ mPowerStatsExporterEnabled = enabled;
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
index f40eef2ed820..ed9414ff53a1 100644
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
@@ -64,7 +64,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
private PowerStats.Descriptor mLastUsedDescriptor;
// Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
// mLastUsedDescriptor changes
- private CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
// Sequence of steps for power estimation and intermediate results.
private PowerEstimationPlan mPlan;
@@ -106,7 +106,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
}
mLastUsedDescriptor = descriptor;
- mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
mStatsLayout.fromExtras(descriptor.extras);
mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
@@ -149,7 +149,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
- if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) {
+ if (mStatsLayout.getEnergyConsumerCount() != 0) {
initEnergyConsumerToPowerBracketMaps();
}
}
@@ -212,7 +212,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
* CL_2: [bracket3, bracket4]
*/
private void initEnergyConsumerToPowerBracketMaps() {
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
@@ -294,7 +294,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
continue;
}
- intermediates.uptime += mStatsLayout.getUptime(mTmpDeviceStatsArray);
+ intermediates.uptime += mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
intermediates.timeByCluster[cluster] +=
@@ -351,7 +351,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
@@ -392,7 +392,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
private void adjustEstimatesUsingEnergyConsumers(
Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
if (energyConsumerCount == 0) {
return;
}
@@ -509,8 +509,8 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces
}
sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
}
- sb.append("] uptime: ").append(mStatsLayout.getUptime(stats));
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats));
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
if (energyConsumerCount > 0) {
sb.append(" energy: [");
for (int i = 0; i < energyConsumerCount; i++) {
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index b8e581f32a58..c05407cb6d17 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -22,6 +22,7 @@ import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.PersistableBundle;
+import android.os.Process;
import android.power.PowerStatsInternal;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,6 +68,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
private final CpuScalingPolicies mCpuScalingPolicies;
private final PowerProfile mPowerProfile;
private final KernelCpuStatsReader mKernelCpuStatsReader;
+ private final PowerStatsUidResolver mUidResolver;
private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
private final IntSupplier mVoltageSupplier;
private final int mDefaultCpuPowerBrackets;
@@ -81,7 +83,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
- private StatsArrayLayout mLayout;
+ private CpuStatsArrayLayout mLayout;
private long mLastUpdateTimestampNanos;
private long mLastUpdateUptimeMillis;
private int mLastVoltageMv;
@@ -91,55 +93,30 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
* Captures the positions and lengths of sections of the stats array, such as time-in-state,
* power usage estimates etc.
*/
- public static class StatsArrayLayout {
+ public static class CpuStatsArrayLayout extends StatsArrayLayout {
private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
- private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
- private static final String EXTRA_DEVICE_UPTIME_POSITION = "du";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
- private static final String EXTRA_UID_POWER_POSITION = "up";
-
- private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
-
- private int mDeviceStatsArrayLength;
- private int mUidStatsArrayLength;
private int mDeviceCpuTimeByScalingStepPosition;
private int mDeviceCpuTimeByScalingStepCount;
private int mDeviceCpuTimeByClusterPosition;
private int mDeviceCpuTimeByClusterCount;
- private int mDeviceCpuUptimePosition;
- private int mDeviceEnergyConsumerPosition;
- private int mDeviceEnergyConsumerCount;
- private int mDevicePowerEstimatePosition;
private int mUidPowerBracketsPosition;
private int mUidPowerBracketCount;
- private int[][] mEnergyConsumerToPowerBucketMaps;
- private int mUidPowerEstimatePosition;
private int[] mScalingStepToPowerBracketMap;
- public int getDeviceStatsArrayLength() {
- return mDeviceStatsArrayLength;
- }
-
- public int getUidStatsArrayLength() {
- return mUidStatsArrayLength;
- }
-
/**
* Declare that the stats array has a section capturing CPU time per scaling step
*/
public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
- mDeviceCpuTimeByScalingStepPosition = mDeviceStatsArrayLength;
+ mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
mDeviceCpuTimeByScalingStepCount = scalingStepCount;
- mDeviceStatsArrayLength += scalingStepCount;
}
public int getCpuScalingStepCount() {
@@ -166,9 +143,8 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
* Declare that the stats array has a section capturing CPU time in each cluster
*/
public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+ mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
mDeviceCpuTimeByClusterCount = clusterCount;
- mDeviceCpuTimeByClusterPosition = mDeviceStatsArrayLength;
- mDeviceStatsArrayLength += clusterCount;
}
public int getCpuClusterCount() {
@@ -192,86 +168,12 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
}
/**
- * Declare that the stats array has a section capturing CPU uptime
- */
- public void addDeviceSectionUptime() {
- mDeviceCpuUptimePosition = mDeviceStatsArrayLength++;
- }
-
- /**
- * Saves the CPU uptime duration in the corresponding <code>stats</code> element.
- */
- public void setUptime(long[] stats, long value) {
- stats[mDeviceCpuUptimePosition] = value;
- }
-
- /**
- * Extracts the CPU uptime duration from the corresponding <code>stats</code> element.
- */
- public long getUptime(long[] stats) {
- return stats[mDeviceCpuUptimePosition];
- }
-
- /**
- * Declares that the stats array has a section capturing EnergyConsumer data from
- * PowerStatsService.
- */
- public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength;
- mDeviceEnergyConsumerCount = energyConsumerCount;
- mDeviceStatsArrayLength += energyConsumerCount;
- }
-
- public int getCpuClusterEnergyConsumerCount() {
- return mDeviceEnergyConsumerCount;
- }
-
- /**
- * Saves the accumulated energy for the specified rail the corresponding
- * <code>stats</code> element.
- */
- public void setConsumedEnergy(long[] stats, int index, long energy) {
- stats[mDeviceEnergyConsumerPosition + index] = energy;
- }
-
- /**
- * Extracts the EnergyConsumer data from a device stats array for the specified
- * EnergyConsumer.
- */
- public long getConsumedEnergy(long[] stats, int index) {
- return stats[mDeviceEnergyConsumerPosition + index];
- }
-
- /**
- * Declare that the stats array has a section capturing a power estimate
- */
- public void addDeviceSectionPowerEstimate() {
- mDevicePowerEstimatePosition = mDeviceStatsArrayLength++;
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setDevicePowerEstimate(long[] stats, double power) {
- stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a device stats array and converts it to mAh.
- */
- public double getDevicePowerEstimate(long[] stats) {
- return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
* Declare that the UID stats array has a section capturing CPU time per power bracket.
*/
public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
- mUidPowerBracketsPosition = mUidStatsArrayLength;
updatePowerBracketCount();
- mUidStatsArrayLength += mUidPowerBracketCount;
+ mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
}
private void updatePowerBracketCount() {
@@ -306,31 +208,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
}
/**
- * Declare that the UID stats array has a section capturing a power estimate
- */
- public void addUidSectionPowerEstimate() {
- mUidPowerEstimatePosition = mUidStatsArrayLength++;
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setUidPowerEstimate(long[] stats, double power) {
- stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a UID stats array and converts it to mAh.
- */
- public double getUidPowerEstimate(long[] stats) {
- return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
* Copies the elements of the stats array layout into <code>extras</code>
*/
public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
mDeviceCpuTimeByScalingStepPosition);
extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
@@ -339,22 +220,16 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
mDeviceCpuTimeByClusterPosition);
extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
mDeviceCpuTimeByClusterCount);
- extras.putInt(EXTRA_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
- mDeviceEnergyConsumerPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
- mDeviceEnergyConsumerCount);
- extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
- extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
mScalingStepToPowerBracketMap);
- extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
}
/**
* Retrieves elements of the stats array layout from <code>extras</code>
*/
public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
mDeviceCpuTimeByScalingStepPosition =
extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
mDeviceCpuTimeByScalingStepCount =
@@ -363,43 +238,39 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
mDeviceCpuTimeByClusterCount =
extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
- mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION);
- mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
- mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
- mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
mScalingStepToPowerBracketMap =
- extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
if (mScalingStepToPowerBracketMap == null) {
mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
}
updatePowerBracketCount();
- mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
}
}
public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- IntSupplier voltageSupplier, Handler handler, long throttlePeriodMs) {
- this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(),
+ PowerStatsUidResolver uidResolver, IntSupplier voltageSupplier, Handler handler,
+ long throttlePeriodMs) {
+ this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), uidResolver,
() -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier,
throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS,
DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER);
}
public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
- Supplier<PowerStatsInternal> powerStatsSupplier,
+ Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
+ PowerStatsUidResolver uidResolver, Supplier<PowerStatsInternal> powerStatsSupplier,
IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
super(handler, throttlePeriodMs, clock);
mCpuScalingPolicies = cpuScalingPolicies;
mPowerProfile = powerProfile;
mKernelCpuStatsReader = kernelCpuStatsReader;
+ mUidResolver = uidResolver;
mPowerStatsSupplier = powerStatsSupplier;
mVoltageSupplier = voltageSupplier;
mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
-
}
/**
@@ -409,13 +280,13 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
setEnabled(Flags.streamlinedBatteryStats());
}
- private void ensureInitialized() {
+ private boolean ensureInitialized() {
if (mIsInitialized) {
- return;
+ return true;
}
if (!isEnabled()) {
- return;
+ return false;
}
mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
@@ -432,10 +303,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
int[] scalingStepToPowerBracketMap = initPowerBrackets();
- mLayout = new StatsArrayLayout();
+ mLayout = new CpuStatsArrayLayout();
mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
- mLayout.addDeviceSectionUptime();
+ mLayout.addDeviceSectionUsageDuration();
mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
mLayout.addDeviceSectionPowerEstimate();
mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
@@ -451,6 +322,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
mIsInitialized = true;
+ return true;
}
private void readCpuEnergyConsumerIds() {
@@ -590,7 +462,9 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
* Prints the definitions of power brackets.
*/
public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return;
+ }
if (mLayout == null) {
return;
@@ -610,7 +484,9 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
*/
@VisibleForTesting
public String getCpuPowerBracketDescription(int powerBracket) {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return "";
+ }
int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap();
StringBuilder sb = new StringBuilder();
@@ -647,14 +523,18 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
*/
@VisibleForTesting
public PowerStats.Descriptor getPowerStatsDescriptor() {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return null;
+ }
return mPowerStatsDescriptor;
}
@Override
protected PowerStats collectStats() {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return null;
+ }
if (!mIsPerUidTimeInStateSupported) {
return null;
@@ -682,7 +562,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
if (uptimeDelta > mCpuPowerStats.durationMs) {
uptimeDelta = mCpuPowerStats.durationMs;
}
- mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta);
+ mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta);
if (mCpuEnergyConsumerIds.length != 0) {
collectEnergyConsumers();
@@ -761,7 +641,21 @@ public class CpuPowerStatsCollector extends PowerStatsCollector {
uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket];
}
if (nonzero) {
- mCpuPowerStats.uidStats.put(uid, uidStats.stats);
+ int ownerUid;
+ if (Process.isSdkSandboxUid(uid)) {
+ ownerUid = Process.getAppUidForSdkSandboxUid(uid);
+ } else {
+ ownerUid = mUidResolver.mapUid(uid);
+ }
+
+ long[] ownerStats = mCpuPowerStats.uidStats.get(ownerUid);
+ if (ownerStats == null) {
+ mCpuPowerStats.uidStats.put(ownerUid, uidStats.stats);
+ } else {
+ for (int i = 0; i < ownerStats.length; i++) {
+ ownerStats[i] += uidStats.stats[i];
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 2c7843e626c9..0facb9c01d74 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -44,6 +44,7 @@ class PowerComponentAggregatedPowerStats {
private static final String XML_TAG_DEVICE_STATS = "device-stats";
private static final String XML_TAG_UID_STATS = "uid-stats";
private static final String XML_ATTR_UID = "uid";
+ private static final long UNKNOWN = -1;
public final int powerComponentId;
private final MultiStateStats.States[] mDeviceStateConfig;
@@ -51,17 +52,16 @@ class PowerComponentAggregatedPowerStats {
@NonNull
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
private final int[] mDeviceStates;
- private final long[] mDeviceStateTimestamps;
private MultiStateStats.Factory mStatsFactory;
private MultiStateStats.Factory mUidStatsFactory;
private PowerStats.Descriptor mPowerStatsDescriptor;
+ private long mPowerStatsTimestamp;
private MultiStateStats mDeviceStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private static class UidStats {
public int[] states;
- public long[] stateTimestampMs;
public MultiStateStats stats;
}
@@ -71,7 +71,7 @@ class PowerComponentAggregatedPowerStats {
mDeviceStateConfig = config.getDeviceStateConfig();
mUidStateConfig = config.getUidStateConfig();
mDeviceStates = new int[mDeviceStateConfig.length];
- mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
+ mPowerStatsTimestamp = UNKNOWN;
}
@NonNull
@@ -85,8 +85,11 @@ class PowerComponentAggregatedPowerStats {
}
void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) {
+ if (mDeviceStats == null) {
+ createDeviceStats();
+ }
+
mDeviceStates[stateId] = state;
- mDeviceStateTimestamps[stateId] = time;
if (mDeviceStateConfig[stateId].isTracked()) {
if (mDeviceStats != null) {
@@ -97,6 +100,11 @@ class PowerComponentAggregatedPowerStats {
if (mUidStateConfig[stateId].isTracked()) {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+
+ uidStats.states[stateId] = state;
if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
}
@@ -111,8 +119,11 @@ class PowerComponentAggregatedPowerStats {
}
UidStats uidStats = getUidStats(uid);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+
uidStats.states[stateId] = state;
- uidStats.stateTimestampMs[stateId] = time;
if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
@@ -150,10 +161,11 @@ class PowerComponentAggregatedPowerStats {
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
}
+
+ mPowerStatsTimestamp = timestampMs;
}
void reset() {
- mPowerStatsDescriptor = null;
mStatsFactory = null;
mUidStatsFactory = null;
mDeviceStats = null;
@@ -163,12 +175,10 @@ class PowerComponentAggregatedPowerStats {
}
private UidStats getUidStats(int uid) {
- // TODO(b/292247660): map isolated and sandbox UIDs
UidStats uidStats = mUidStats.get(uid);
if (uidStats == null) {
uidStats = new UidStats();
uidStats.states = new int[mUidStateConfig.length];
- uidStats.stateTimestampMs = new long[mUidStateConfig.length];
mUidStats.put(uid, uidStats);
}
return uidStats;
@@ -209,42 +219,38 @@ class PowerComponentAggregatedPowerStats {
return false;
}
- private boolean createDeviceStats() {
+ private void createDeviceStats() {
if (mStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
- return false;
+ return;
}
mStatsFactory = new MultiStateStats.Factory(
mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
}
mDeviceStats = mStatsFactory.create();
- for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- mDeviceStats.setState(stateId, mDeviceStates[stateId],
- mDeviceStateTimestamps[stateId]);
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
+ mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp);
+ }
}
- return true;
}
- private boolean createUidStats(UidStats uidStats) {
+ private void createUidStats(UidStats uidStats) {
if (mUidStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
- return false;
+ return;
}
mUidStatsFactory = new MultiStateStats.Factory(
mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
}
uidStats.stats = mUidStatsFactory.create();
- for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- uidStats.stats.setState(stateId, mDeviceStates[stateId],
- mDeviceStateTimestamps[stateId]);
- }
- for (int stateId = mDeviceStateConfig.length; stateId < mUidStateConfig.length; stateId++) {
- uidStats.stats.setState(stateId, uidStats.states[stateId],
- uidStats.stateTimestampMs[stateId]);
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp);
+ }
}
- return true;
}
public void writeXml(TypedXmlSerializer serializer) throws IOException {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index 2f9d5674d78a..3f88a2d2dec0 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -29,13 +29,18 @@ import java.util.function.Consumer;
* {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history.
*/
public class PowerStatsAggregator {
+ private static final long UNINITIALIZED = -1;
private final AggregatedPowerStats mStats;
+ private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private final BatteryStatsHistory mHistory;
private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
+ private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+ private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
BatteryStatsHistory history) {
mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
mHistory = history;
for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
@@ -44,6 +49,10 @@ public class PowerStatsAggregator {
}
}
+ AggregatedPowerStatsConfig getConfig() {
+ return mAggregatedPowerStatsConfig;
+ }
+
/**
* Iterates of the battery history and aggregates power stats between the specified times.
* The start and end are specified in the battery-stats monotonic time, which is the
@@ -58,18 +67,20 @@ public class PowerStatsAggregator {
*/
public void aggregatePowerStats(long startTimeMs, long endTimeMs,
Consumer<AggregatedPowerStats> consumer) {
- int currentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
- int currentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- long baseTime = -1;
+ boolean clockUpdateAdded = false;
+ long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
long lastTime = 0;
try (BatteryStatsHistoryIterator iterator =
mHistory.copy().iterate(startTimeMs, endTimeMs)) {
while (iterator.hasNext()) {
BatteryStats.HistoryItem item = iterator.next();
- if (baseTime < 0) {
+ if (!clockUpdateAdded) {
mStats.addClockUpdate(item.time, item.currentTime);
- baseTime = item.time;
+ if (baseTime == UNINITIALIZED) {
+ baseTime = item.time;
+ }
+ clockUpdateAdded = true;
} else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
|| item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
mStats.addClockUpdate(item.time, item.currentTime);
@@ -81,20 +92,20 @@ public class PowerStatsAggregator {
(item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
? AggregatedPowerStatsConfig.POWER_STATE_OTHER
: AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
- if (batteryState != currentBatteryState) {
+ if (batteryState != mCurrentBatteryState) {
mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState,
item.time);
- currentBatteryState = batteryState;
+ mCurrentBatteryState = batteryState;
}
int screenState =
(item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
? AggregatedPowerStatsConfig.SCREEN_STATE_ON
: AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- if (screenState != currentScreenState) {
+ if (screenState != mCurrentScreenState) {
mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState,
item.time);
- currentScreenState = screenState;
+ mCurrentScreenState = screenState;
}
if (item.processStateChange != null) {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index 84cc21e81536..abfe9debc7de 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -16,10 +16,13 @@
package com.android.server.power.stats;
+import android.annotation.Nullable;
import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.util.FastImmutableArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Clock;
@@ -38,6 +41,7 @@ import java.util.stream.Stream;
* except where noted.
*/
public abstract class PowerStatsCollector {
+ private static final String TAG = "PowerStatsCollector";
private static final int MILLIVOLTS_PER_VOLT = 1000;
private final Handler mHandler;
protected final Clock mClock;
@@ -46,6 +50,200 @@ public abstract class PowerStatsCollector {
private boolean mEnabled;
private long mLastScheduledUpdateMs = -1;
+ /**
+ * Captures the positions and lengths of sections of the stats array, such as usage duration,
+ * power usage estimates etc.
+ */
+ public static class StatsArrayLayout {
+ private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+ private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+ private static final String EXTRA_UID_POWER_POSITION = "up";
+
+ protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+
+ private int mDeviceStatsArrayLength;
+ private int mUidStatsArrayLength;
+
+ protected int mDeviceDurationPosition;
+ private int mDeviceEnergyConsumerPosition;
+ private int mDeviceEnergyConsumerCount;
+ private int mDevicePowerEstimatePosition;
+ private int mUidPowerEstimatePosition;
+
+ public int getDeviceStatsArrayLength() {
+ return mDeviceStatsArrayLength;
+ }
+
+ public int getUidStatsArrayLength() {
+ return mUidStatsArrayLength;
+ }
+
+ protected int addDeviceSection(int length) {
+ int position = mDeviceStatsArrayLength;
+ mDeviceStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addUidSection(int length) {
+ int position = mUidStatsArrayLength;
+ mUidStatsArrayLength += length;
+ return position;
+ }
+
+ /**
+ * Declare that the stats array has a section capturing usage duration
+ */
+ public void addDeviceSectionUsageDuration() {
+ mDeviceDurationPosition = addDeviceSection(1);
+ }
+
+ /**
+ * Saves the usage duration in the corresponding <code>stats</code> element.
+ */
+ public void setUsageDuration(long[] stats, long value) {
+ stats[mDeviceDurationPosition] = value;
+ }
+
+ /**
+ * Extracts the usage duration from the corresponding <code>stats</code> element.
+ */
+ public long getUsageDuration(long[] stats) {
+ return stats[mDeviceDurationPosition];
+ }
+
+ /**
+ * Declares that the stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+ mDeviceEnergyConsumerCount = energyConsumerCount;
+ }
+
+ public int getEnergyConsumerCount() {
+ return mDeviceEnergyConsumerCount;
+ }
+
+ /**
+ * Saves the accumulated energy for the specified rail the corresponding
+ * <code>stats</code> element.
+ */
+ public void setConsumedEnergy(long[] stats, int index, long energy) {
+ stats[mDeviceEnergyConsumerPosition + index] = energy;
+ }
+
+ /**
+ * Extracts the EnergyConsumer data from a device stats array for the specified
+ * EnergyConsumer.
+ */
+ public long getConsumedEnergy(long[] stats, int index) {
+ return stats[mDeviceEnergyConsumerPosition + index];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing a power estimate
+ */
+ public void addDeviceSectionPowerEstimate() {
+ mDevicePowerEstimatePosition = addDeviceSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setDevicePowerEstimate(long[] stats, double power) {
+ stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a device stats array and converts it to mAh.
+ */
+ public double getDevicePowerEstimate(long[] stats) {
+ return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing a power estimate
+ */
+ public void addUidSectionPowerEstimate() {
+ mUidPowerEstimatePosition = addUidSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setUidPowerEstimate(long[] stats, double power) {
+ stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a UID stats array and converts it to mAh.
+ */
+ public double getUidPowerEstimate(long[] stats) {
+ return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
+ mDeviceEnergyConsumerPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
+ mDeviceEnergyConsumerCount);
+ extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+
+ protected void putIntArray(PersistableBundle extras, String key, int[] array) {
+ if (array == null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int value : array) {
+ if (!sb.isEmpty()) {
+ sb.append(',');
+ }
+ sb.append(value);
+ }
+ extras.putString(key, sb.toString());
+ }
+
+ protected int[] getIntArray(PersistableBundle extras, String key) {
+ String string = extras.getString(key);
+ if (string == null) {
+ return null;
+ }
+ String[] values = string.trim().split(",");
+ int[] result = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ try {
+ result[i] = Integer.parseInt(values[i]);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid CSV format: " + string);
+ return null;
+ }
+ }
+ return result;
+ }
+ }
+
@GuardedBy("this")
@SuppressWarnings("unchecked")
private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList =
@@ -141,6 +339,7 @@ public abstract class PowerStatsCollector {
return true;
}
+ @Nullable
protected abstract PowerStats collectStats();
/**
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
new file mode 100644
index 000000000000..70c24d58bb2a
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
+import android.util.Slog;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Given a time range, converts accumulated PowerStats to BatteryUsageStats. Combines
+ * stores spans of PowerStats with the yet-unprocessed tail of battery history.
+ */
+public class PowerStatsExporter {
+ private static final String TAG = "PowerStatsExporter";
+ private final PowerStatsStore mPowerStatsStore;
+ private final PowerStatsAggregator mPowerStatsAggregator;
+ private final long mBatterySessionTimeSpanSlackMillis;
+ private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2);
+
+ public PowerStatsExporter(PowerStatsStore powerStatsStore,
+ PowerStatsAggregator powerStatsAggregator) {
+ this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS);
+ }
+
+ public PowerStatsExporter(PowerStatsStore powerStatsStore,
+ PowerStatsAggregator powerStatsAggregator,
+ long batterySessionTimeSpanSlackMillis) {
+ mPowerStatsStore = powerStatsStore;
+ mPowerStatsAggregator = powerStatsAggregator;
+ mBatterySessionTimeSpanSlackMillis = batterySessionTimeSpanSlackMillis;
+ }
+
+ /**
+ * Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated
+ * PowerStats, both stored in PowerStatsStore and not-yet processed.
+ */
+ public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ long monotonicStartTime, long monotonicEndTime) {
+ long maxEndTime = monotonicStartTime;
+ List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
+ for (int i = spans.size() - 1; i >= 0; i--) {
+ PowerStatsSpan.Metadata metadata = spans.get(i);
+ if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+ continue;
+ }
+
+ List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
+ long spanMinTime = Long.MAX_VALUE;
+ long spanMaxTime = Long.MIN_VALUE;
+ for (int j = 0; j < timeFrames.size(); j++) {
+ PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
+ long startMonotonicTime = timeFrame.startMonotonicTime;
+ long endMonotonicTime = startMonotonicTime + timeFrame.duration;
+ if (startMonotonicTime < spanMinTime) {
+ spanMinTime = startMonotonicTime;
+ }
+ if (endMonotonicTime > spanMaxTime) {
+ spanMaxTime = endMonotonicTime;
+ }
+ }
+
+ if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
+ continue;
+ }
+
+ if (spanMaxTime > maxEndTime) {
+ maxEndTime = spanMaxTime;
+ }
+
+ PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+ AggregatedPowerStatsSection.TYPE);
+ if (span == null) {
+ Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
+ continue;
+ }
+ List<PowerStatsSpan.Section> sections = span.getSections();
+ for (int k = 0; k < sections.size(); k++) {
+ PowerStatsSpan.Section section = sections.get(k);
+ populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
+ ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
+ }
+ }
+
+ if (maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
+ mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
+ stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
+ }
+ }
+
+ private void populateBatteryUsageStatsBuilder(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) {
+ AggregatedPowerStatsConfig config = mPowerStatsAggregator.getConfig();
+ List<AggregatedPowerStatsConfig.PowerComponent> powerComponents =
+ config.getPowerComponentsAggregatedStatsConfigs();
+ for (int i = powerComponents.size() - 1; i >= 0; i--) {
+ populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats,
+ powerComponents.get(i));
+ }
+ }
+
+ private void populateBatteryUsageStatsBuilder(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats,
+ AggregatedPowerStatsConfig.PowerComponent powerComponent) {
+ int powerComponentId = powerComponent.getPowerComponentId();
+ PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
+ powerComponentId);
+ if (powerComponentStats == null) {
+ return;
+ }
+
+ PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
+ if (descriptor == null) {
+ return;
+ }
+
+ PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout();
+ layout.fromExtras(descriptor.extras);
+
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ double[] totalPower = new double[1];
+ MultiStateStats.States.forEachTrackedStateCombination(powerComponent.getDeviceStateConfig(),
+ states -> {
+ if (states[AggregatedPowerStatsConfig.STATE_POWER]
+ != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
+ return;
+ }
+
+ if (!powerComponentStats.getDeviceStats(deviceStats, states)) {
+ return;
+ }
+
+ totalPower[0] += layout.getDevicePowerEstimate(deviceStats);
+ });
+
+ AggregateBatteryConsumer.Builder deviceScope =
+ batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ deviceScope.addConsumedPower(powerComponentId,
+ totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ ArrayList<Integer> uids = new ArrayList<>();
+ powerComponentStats.collectUids(uids);
+
+ boolean breakDownByProcState =
+ batteryUsageStatsBuilder.isProcessStateDataNeeded()
+ && powerComponent
+ .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
+ .isTracked();
+
+ double[] powerByProcState =
+ new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
+ double powerAllApps = 0;
+ for (int uid : uids) {
+ UidBatteryConsumer.Builder builder =
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+ Arrays.fill(powerByProcState, 0);
+
+ MultiStateStats.States.forEachTrackedStateCombination(
+ powerComponent.getUidStateConfig(),
+ states -> {
+ if (states[AggregatedPowerStatsConfig.STATE_POWER]
+ != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
+ return;
+ }
+
+ if (!powerComponentStats.getUidStats(uidStats, uid, states)) {
+ return;
+ }
+
+ double power = layout.getUidPowerEstimate(uidStats);
+ int procState = breakDownByProcState
+ ? states[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
+ : BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+ powerByProcState[procState] += power;
+ });
+
+ double powerAllProcStates = 0;
+ for (int procState = 0; procState < powerByProcState.length; procState++) {
+ double power = powerByProcState[procState];
+ if (power == 0) {
+ continue;
+ }
+ powerAllProcStates += power;
+ if (breakDownByProcState
+ && procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ builder.addConsumedPower(builder.getKey(powerComponentId, procState),
+ power, BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
+ }
+ builder.addConsumedPower(powerComponentId, powerAllProcStates,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ powerAllApps += powerAllProcStates;
+ }
+
+ AggregateBatteryConsumer.Builder allAppsScope =
+ batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 551302ee8f14..97d872a1a539 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -19,8 +19,6 @@ package com.android.server.power.stats;
import android.annotation.DurationMillisLong;
import android.app.AlarmManager;
import android.content.Context;
-import android.os.BatteryUsageStats;
-import android.os.BatteryUsageStatsQuery;
import android.os.ConditionVariable;
import android.os.Handler;
import android.util.IndentingPrintWriter;
@@ -52,7 +50,6 @@ public class PowerStatsScheduler {
private final MonotonicClock mMonotonicClock;
private final Handler mHandler;
private final BatteryStatsImpl mBatteryStats;
- private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private final PowerStatsAggregator mPowerStatsAggregator;
private long mLastSavedSpanEndMonotonicTime;
@@ -60,7 +57,7 @@ public class PowerStatsScheduler {
@DurationMillisLong long aggregatedPowerStatsSpanDuration,
@DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
Clock clock, MonotonicClock monotonicClock, Handler handler,
- BatteryStatsImpl batteryStats, BatteryUsageStatsProvider batteryUsageStatsProvider) {
+ BatteryStatsImpl batteryStats) {
mContext = context;
mPowerStatsAggregator = powerStatsAggregator;
mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
@@ -70,16 +67,15 @@ public class PowerStatsScheduler {
mMonotonicClock = monotonicClock;
mHandler = handler;
mBatteryStats = batteryStats;
- mBatteryUsageStatsProvider = batteryUsageStatsProvider;
}
/**
* Kicks off the scheduling of power stats aggregation spans.
*/
public void start(boolean enablePeriodicPowerStatsCollection) {
- mBatteryStats.setBatteryResetListener(this::storeBatteryUsageStatsOnReset);
mEnablePeriodicPowerStatsCollection = enablePeriodicPowerStatsCollection;
if (mEnablePeriodicPowerStatsCollection) {
+ schedulePowerStatsAggregation();
scheduleNextPowerStatsAggregation();
}
}
@@ -235,28 +231,6 @@ public class PowerStatsScheduler {
mPowerStatsStore.storeAggregatedPowerStats(stats);
}
- private void storeBatteryUsageStatsOnReset(int resetReason) {
- if (resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
- return;
- }
-
- final BatteryUsageStats batteryUsageStats =
- mBatteryUsageStatsProvider.getBatteryUsageStats(
- new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includePowerModels()
- .includeProcessStateData()
- .build());
-
- // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
- // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
- // start time
- long monotonicStartTime =
- mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
- mHandler.post(() ->
- mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
- }
-
private void awaitCompletion() {
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java b/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java
new file mode 100644
index 000000000000..8dc360983239
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Maintains a map of isolated UIDs to their respective owner UIDs, to support combining
+ * power stats for isolated UIDs, which are typically short-lived, into the corresponding app UID.
+ */
+public class PowerStatsUidResolver {
+ private static final String TAG = "PowerStatsUidResolver";
+
+ /**
+ * Listener notified when isolated UIDs are created and removed.
+ */
+ public interface Listener {
+
+ /**
+ * Callback invoked when a new isolated UID is registered.
+ */
+ void onIsolatedUidAdded(int isolatedUid, int parentUid);
+
+ /**
+ * Callback invoked before an isolated UID is evicted from the resolver.
+ * If the listener calls {@link PowerStatsUidResolver#retainIsolatedUid}, the mapping
+ * will be retained until {@link PowerStatsUidResolver#releaseIsolatedUid} is called.
+ */
+ void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid);
+
+ /**
+ * Callback invoked when an isolated UID to owner UID mapping is removed.
+ */
+ void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid);
+ }
+
+ /**
+ * Mapping isolated uids to the actual owning app uid.
+ */
+ private final SparseIntArray mIsolatedUids = new SparseIntArray();
+
+ /**
+ * Internal reference count of isolated uids.
+ */
+ private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+
+ // Keep the list read-only in order to avoid locking during the delivery of listener calls.
+ private volatile List<Listener> mListeners = Collections.emptyList();
+
+ /**
+ * Adds a listener.
+ */
+ public void addListener(Listener listener) {
+ synchronized (this) {
+ List<Listener> newList = new ArrayList<>(mListeners);
+ newList.add(listener);
+ mListeners = Collections.unmodifiableList(newList);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ */
+ public void removeListener(Listener listener) {
+ synchronized (this) {
+ List<Listener> newList = new ArrayList<>(mListeners);
+ newList.remove(listener);
+ mListeners = Collections.unmodifiableList(newList);
+ }
+ }
+
+ /**
+ * Remembers the connection between a newly created isolated UID and its owner app UID.
+ * Calls {@link Listener#onIsolatedUidAdded} on each registered listener.
+ */
+ public void noteIsolatedUidAdded(int isolatedUid, int parentUid) {
+ synchronized (this) {
+ mIsolatedUids.put(isolatedUid, parentUid);
+ mIsolatedUidRefCounts.put(isolatedUid, 1);
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onIsolatedUidAdded(isolatedUid, parentUid);
+ }
+ }
+
+ /**
+ * Handles the removal of an isolated UID by invoking
+ * {@link Listener#onBeforeIsolatedUidRemoved} on each registered listener and the releases
+ * the UID, see {@link #releaseIsolatedUid}.
+ */
+ public void noteIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ synchronized (this) {
+ int curUid = mIsolatedUids.get(isolatedUid, -1);
+ if (curUid != parentUid) {
+ Slog.wtf(TAG, "Attempt to remove an isolated UID " + isolatedUid
+ + " with the parent UID " + parentUid
+ + ". The registered parent UID is " + curUid);
+ return;
+ }
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onBeforeIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+
+ releaseIsolatedUid(isolatedUid);
+ }
+
+ /**
+ * Increments the ref count for an isolated uid.
+ * Call #releaseIsolatedUid to decrement.
+ */
+ public void retainIsolatedUid(int uid) {
+ synchronized (this) {
+ final int refCount = mIsolatedUidRefCounts.get(uid, 0);
+ if (refCount <= 0) {
+ // Uid is not mapped or referenced
+ Slog.w(TAG,
+ "Attempted to increment ref counted of untracked isolated uid (" + uid
+ + ")");
+ return;
+ }
+ mIsolatedUidRefCounts.put(uid, refCount + 1);
+ }
+ }
+
+ /**
+ * Decrements the ref count for the given isolated UID. If the ref count drops to zero,
+ * removes the mapping and calls {@link Listener#onAfterIsolatedUidRemoved} on each registered
+ * listener.
+ */
+ public void releaseIsolatedUid(int isolatedUid) {
+ int parentUid;
+ synchronized (this) {
+ final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
+ if (refCount > 0) {
+ // Isolated uid is still being tracked
+ mIsolatedUidRefCounts.put(isolatedUid, refCount);
+ return;
+ }
+
+ final int idx = mIsolatedUids.indexOfKey(isolatedUid);
+ if (idx >= 0) {
+ parentUid = mIsolatedUids.valueAt(idx);
+ mIsolatedUids.removeAt(idx);
+ mIsolatedUidRefCounts.delete(isolatedUid);
+ } else {
+ Slog.w(TAG, "Attempted to remove untracked child uid (" + isolatedUid + ")");
+ return;
+ }
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onAfterIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+ }
+
+ /**
+ * Releases all isolated UIDs in the specified range, both ends inclusive.
+ */
+ public void releaseUidsInRange(int startUid, int endUid) {
+ IntArray toRelease;
+ synchronized (this) {
+ int startIndex = mIsolatedUids.indexOfKey(startUid);
+ int endIndex = mIsolatedUids.indexOfKey(endUid);
+
+ if (startIndex < 0) {
+ startIndex = ~startIndex;
+ }
+
+ if (endIndex < 0) {
+ // In this ~endIndex is pointing just past where endUid would be, so we must -1.
+ endIndex = ~endIndex - 1;
+ }
+
+ if (startIndex > endIndex) {
+ return;
+ }
+
+ toRelease = new IntArray(endIndex - startIndex);
+ for (int i = startIndex; i <= endIndex; i++) {
+ toRelease.add(mIsolatedUids.keyAt(i));
+ }
+ }
+
+ for (int i = toRelease.size() - 1; i >= 0; i--) {
+ releaseIsolatedUid(toRelease.get(i));
+ }
+ }
+
+ /**
+ * Given an isolated UID, returns the corresponding owner UID. For a non-isolated
+ * UID, returns the UID itself.
+ */
+ public int mapUid(int uid) {
+ synchronized (this) {
+ return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid);
+ }
+ }
+
+ /**
+ * Dumps the current contents of the resolver for the sake of dumpsys.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("Currently mapped isolated uids:");
+ synchronized (this) {
+ final int numIsolatedUids = mIsolatedUids.size();
+ for (int i = 0; i < numIsolatedUids; i++) {
+ final int isolatedUid = mIsolatedUids.keyAt(i);
+ final int ownerUid = mIsolatedUids.valueAt(i);
+ final int refs = mIsolatedUidRefCounts.get(isolatedUid);
+ pw.println(" " + isolatedUid + "->" + ownerUid + " (ref count = " + refs + ")");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 0656a6a7d3e9..59766ec7a175 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -35,6 +35,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
+import static android.hardware.SensorPrivacyManager.EXTRA_NOTIFICATION_ID;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
import static android.hardware.SensorPrivacyManager.EXTRA_TOGGLE_TYPE;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
@@ -164,6 +165,7 @@ public final class SensorPrivacyService extends SystemService {
private final AppOpsManagerInternal mAppOpsManagerInternal;
private final TelephonyManager mTelephonyManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private final NotificationManager mNotificationManager;
private CameraPrivacyLightController mCameraPrivacyLightController;
@@ -188,6 +190,7 @@ public final class SensorPrivacyService extends SystemService {
mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
}
@@ -288,9 +291,18 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void onReceive(Context context, Intent intent) {
setToggleSensorPrivacy(
- ((UserHandle) intent.getParcelableExtra(
- Intent.EXTRA_USER, android.os.UserHandle.class)).getIdentifier(), OTHER,
- intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
+ intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class)
+ .getIdentifier(),
+ OTHER,
+ intent.getIntExtra(EXTRA_SENSOR, UNKNOWN),
+ false
+ );
+
+ int notificationId =
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, SystemMessage.NOTE_UNKNOWN);
+ if (notificationId != SystemMessage.NOTE_UNKNOWN) {
+ mNotificationManager.cancel(notificationId);
+ }
}
}, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY),
MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
@@ -635,8 +647,6 @@ public final class SensorPrivacyService extends SystemService {
notificationId = SystemMessage.NOTE_UNBLOCK_CAM_TOGGLE;
}
- NotificationManager notificationManager =
- mContext.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(
SENSOR_PRIVACY_CHANNEL_ID,
getUiContext().getString(R.string.sensor_privacy_notification_channel_label),
@@ -646,7 +656,7 @@ public final class SensorPrivacyService extends SystemService {
channel.enableVibration(false);
channel.setBlockable(false);
- notificationManager.createNotificationChannel(channel);
+ mNotificationManager.createNotificationChannel(channel);
Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
@@ -669,10 +679,11 @@ public final class SensorPrivacyService extends SystemService {
new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY)
.setPackage(mContext.getPackageName())
.putExtra(EXTRA_SENSOR, sensor)
+ .putExtra(EXTRA_NOTIFICATION_ID, notificationId)
.putExtra(Intent.EXTRA_USER, user),
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT);
- notificationManager.notify(notificationId,
+ mNotificationManager.notify(notificationId,
new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
.setContentTitle(contentTitle)
.setContentText(contentText)
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index e6273d331fce..0467d0cd351d 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -38,6 +38,7 @@ import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
+import android.media.tv.ad.ITvAdManager;
import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
import android.media.tv.interactive.ITvInteractiveAppManager;
@@ -345,6 +346,7 @@ public class TvInteractiveAppManagerService extends SystemService {
Slogf.d(TAG, "onStart");
}
publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
+ publishBinderService(Context.TV_AD_SERVICE, new TvAdBinderService());
}
@Override
@@ -688,6 +690,12 @@ public class TvInteractiveAppManagerService extends SystemService {
}
return session;
}
+ private final class TvAdBinderService extends ITvAdManager.Stub {
+ @Override
+ public void startAdService(IBinder sessionToken, int userId) {
+ }
+
+ }
private final class BinderService extends ITvInteractiveAppManager.Stub {
diff --git a/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
new file mode 100644
index 000000000000..e5423496fb0e
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+import android.util.KeyValueListParser;
+
+/**
+ * Helper class to mediate the value to use when a constant exists in both a key=value pair Settings
+ * constant (that can be parsed by {@link KeyValueListParser})
+ * and the {@link DeviceConfig} properties.
+ */
+public abstract class UserSettingDeviceConfigMediator {
+ private static final String TAG = UserSettingDeviceConfigMediator.class.getSimpleName();
+
+ @Nullable
+ protected DeviceConfig.Properties mProperties;
+ @NonNull
+ protected final KeyValueListParser mSettingsParser;
+
+ /**
+ * @param keyValueListDelimiter The delimiter passed into the {@link KeyValueListParser}.
+ */
+ protected UserSettingDeviceConfigMediator(char keyValueListDelimiter) {
+ mSettingsParser = new KeyValueListParser(keyValueListDelimiter);
+ }
+
+ /**
+ * Sets the key=value list string to read from. Setting {@code null} will clear any previously
+ * set string.
+ */
+ public void setSettingsString(@Nullable String settings) {
+ mSettingsParser.setString(settings);
+ }
+
+ /**
+ * Sets the DeviceConfig Properties to read from. Setting {@code null} will clear any previously
+ * set properties.
+ */
+ public void setDeviceConfigProperties(@Nullable DeviceConfig.Properties properties) {
+ mProperties = properties;
+ }
+
+ /**
+ * Get the value for key as a boolean.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract boolean getBoolean(@NonNull String key, boolean defaultValue);
+
+ /**
+ * Get the value for key as a float.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract float getFloat(@NonNull String key, float defaultValue);
+
+ /**
+ * Get the value for key as an int.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract int getInt(@NonNull String key, int defaultValue);
+
+ /**
+ * Get the value for key as a long.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract long getLong(@NonNull String key, long defaultValue);
+
+ /**
+ * Get the value for key as a String.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract String getString(@NonNull String key, @Nullable String defaultValue);
+
+ /**
+ * A mediator in which the existence of a single settings key-value pair will override usage
+ * of DeviceConfig properties. That is, if the Settings constant has any values set,
+ * then everything in the DeviceConfig namespace will be ignored.
+ */
+ public static class SettingsOverridesAllMediator extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesAllMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getBoolean(key, defaultValue);
+ }
+ return mSettingsParser.getBoolean(key, defaultValue);
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue);
+ }
+ return mSettingsParser.getFloat(key, defaultValue);
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue);
+ }
+ return mSettingsParser.getInt(key, defaultValue);
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue);
+ }
+ return mSettingsParser.getLong(key, defaultValue);
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getString(key, defaultValue);
+ }
+ return mSettingsParser.getString(key, defaultValue);
+ }
+ }
+
+ /**
+ * A mediator in which only individual keys in the DeviceConfig namespace will be overridden
+ * by the same key in the Settings constant. If the Settings constant does not have a specific
+ * key set, then the DeviceConfig value will be used instead.
+ */
+ public static class SettingsOverridesIndividualMediator
+ extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesIndividualMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ return mSettingsParser.getBoolean(key,
+ mProperties == null ? defaultValue : mProperties.getBoolean(key, defaultValue));
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ return mSettingsParser.getFloat(key,
+ mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue));
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ return mSettingsParser.getInt(key,
+ mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue));
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ return mSettingsParser.getLong(key,
+ mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue));
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ return mSettingsParser.getString(key,
+ mProperties == null ? defaultValue : mProperties.getString(key, defaultValue));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index b12da61d0b3f..e9c40964aee4 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -258,6 +258,11 @@ public class WebViewUpdateService extends SystemService {
}
@Override // Binder call
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ return WebViewUpdateService.this.mImpl.getDefaultWebViewPackage();
+ }
+
+ @Override // Binder call
public WebViewProviderInfo[] getAllWebViewPackages() {
return WebViewUpdateService.this.mImpl.getWebViewPackages();
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index cfdef1471f83..60dc4ff224bc 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -17,6 +17,7 @@ package com.android.server.webkit;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -29,8 +30,12 @@ import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -88,6 +93,8 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+ private static final String PIN_GROUP = "webview";
+
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -339,6 +346,34 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
return newPackage;
}
+ private void pinWebviewIfRequired(ApplicationInfo appInfo) {
+ PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+ int webviewPinQuota = pinnerService.getWebviewPinQuota();
+ if (webviewPinQuota <= 0) {
+ return;
+ }
+
+ pinnerService.unpinGroup(PIN_GROUP);
+
+ ArrayList<String> apksToPin = new ArrayList<>();
+ boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
+ for (String sharedLib : appInfo.sharedLibraryFiles) {
+ apksToPin.add(sharedLib);
+ }
+ apksToPin.add(appInfo.sourceDir);
+ if (!pinSharedFirst) {
+ // We want to prioritize pinning of the native library that is most likely used by apps
+ // which in some build flavors live in the main apk and as a shared library for others.
+ Collections.reverse(apksToPin);
+ }
+ for (String apk : apksToPin) {
+ if (webviewPinQuota <= 0) {
+ break;
+ }
+ int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
+ webviewPinQuota -= bytesPinned;
+ }
+ }
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -347,6 +382,7 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
@@ -385,6 +421,13 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
return providers;
}
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ throw new IllegalStateException(
+ "getDefaultWebViewPackage shouldn't be called if update_service_v2 flag is"
+ + " disabled.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 89cb4c802410..29782d9b8b88 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -24,6 +24,7 @@ import android.os.AsyncTask;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
@@ -374,6 +375,23 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
return providers;
}
+ /**
+ * Returns the default WebView provider which should be first availableByDefault option in the
+ * system config.
+ */
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.availableByDefault) {
+ return provider;
+ }
+ }
+ // This should be unreachable because the config parser enforces that there is at least one
+ // availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
index a9c3dc45842f..1772ef9c7405 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
@@ -40,6 +40,8 @@ interface WebViewUpdateServiceInterface {
WebViewProviderInfo[] getValidWebViewPackages();
+ WebViewProviderInfo getDefaultWebViewPackage();
+
PackageInfo getCurrentWebViewPackage();
boolean isMultiProcessEnabled();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 849836828d94..75e6faf97294 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2731,7 +2731,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* Receive the splash screen data from shell, sending to client.
* @param parcelable The data to reconstruct the splash screen view, null mean unable to copy.
*/
- void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
+ void onCopySplashScreenFinish(@Nullable SplashScreenViewParcelable parcelable) {
removeTransferSplashScreenTimeout();
final SurfaceControl windowAnimationLeash = (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6302e08fedb..bbaa6912417c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,6 +1582,7 @@ class ActivityStarter {
// An activity has changed order/visibility or the task is occluded by a transient
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
+ && !transitionController.hasOrderChanges()
&& !transitionController.isTransientHide(startedActivityRootTask)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 34c7eee45f34..6f5c676187e1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3618,8 +3618,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* @hide
*/
@Override
- public void onSplashScreenViewCopyFinished(int taskId, SplashScreenViewParcelable parcelable)
- throws RemoteException {
+ public void onSplashScreenViewCopyFinished(int taskId,
+ @Nullable SplashScreenViewParcelable parcelable)
+ throws RemoteException {
mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
"copySplashScreenViewFinish()");
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 39e900a97021..4625b4fe07ef 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -60,9 +60,9 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import com.android.server.am.PendingIntentRecord;
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.util.HashMap;
@@ -236,6 +236,7 @@ public class BackgroundActivityStartController {
private final @ActivityManager.ProcessState int mCallingUidProcState;
private final boolean mIsCallingUidPersistentSystemProcess;
private final BackgroundStartPrivileges mBalAllowedByPiSender;
+ private final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
private final BackgroundStartPrivileges mBalAllowedByPiCreator;
private final String mRealCallingPackage;
private final int mRealCallingUid;
@@ -267,20 +268,33 @@ public class BackgroundActivityStartController {
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
if (originatingPendingIntent == null) {
- // grant creator BAL privileges unless explicitly opted out
- mBalAllowedByPiCreator =
+ // grant BAL privileges unless explicitly opted out
+ mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
? BackgroundStartPrivileges.NONE
: BackgroundStartPrivileges.ALLOW_BAL;
+ mBalAllowedByPiSender =
+ checkedOptions.getPendingIntentBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
} else {
// for PendingIntents we restrict BAL based on target_sdk
- mBalAllowedByPiCreator = getBackgroundStartPrivilegesAllowedByCreator(
+ mBalAllowedByPiCreatorWithHardening = getBackgroundStartPrivilegesAllowedByCreator(
callingUid, callingPackage, checkedOptions);
+ final BackgroundStartPrivileges mBalAllowedByPiCreatorWithoutHardening =
+ checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ mBalAllowedByPiCreator = balRequireOptInByPendingIntentCreator()
+ ? mBalAllowedByPiCreatorWithHardening
+ : mBalAllowedByPiCreatorWithoutHardening;
+ mBalAllowedByPiSender =
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid, mRealCallingPackage);
}
- mBalAllowedByPiSender =
- PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
- checkedOptions, realCallingUid, mRealCallingPackage);
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -319,10 +333,6 @@ public class BackgroundActivityStartController {
return BackgroundStartPrivileges.NONE;
case ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
// no explicit choice by the app - let us decide what to do
- if (!balRequireOptInByPendingIntentCreator()) {
- // if feature is disabled allow
- return BackgroundStartPrivileges.ALLOW_BAL;
- }
if (callingPackage != null) {
// determine based on the calling/creating package
boolean changeEnabled = CompatChanges.isChangeEnabled(
@@ -367,11 +377,6 @@ public class BackgroundActivityStartController {
return mOriginatingPendingIntent != null && hasRealCaller();
}
- private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
- Preconditions.checkState(!isPendingIntent());
- return dump(resultIfPiCreatorAllowsBal, null);
- }
-
private boolean callerIsRealCaller() {
return mCallingUid == mRealCallingUid;
}
@@ -396,6 +401,8 @@ public class BackgroundActivityStartController {
sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
}
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
+ sb.append("; balAllowedByPiCreatorWithHardening: ")
+ .append(mBalAllowedByPiCreatorWithHardening);
sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
sb.append("; hasRealCaller: ").append(hasRealCaller());
sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -579,11 +586,12 @@ public class BackgroundActivityStartController {
resultForCaller.allows() && resultForRealCaller.blocks());
}
+ // Handle cases with explicit opt-in
if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+ Slog.d(TAG, "Activity start explicitly allowed by caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForCaller, state);
@@ -592,11 +600,12 @@ public class BackgroundActivityStartController {
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+ Slog.d(TAG, "Activity start explicitly allowed by real caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForRealCaller, state);
}
+ // Handle PendingIntent cases with default behavior next
boolean callerCanAllow = resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -716,17 +725,18 @@ public class BackgroundActivityStartController {
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
final boolean appSwitchAllowedOrFg =
appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
- final boolean allowCallingUidStartActivity =
- ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
- && callingUidHasAnyVisibleWindow)
- || isCallingUidPersistentSystemProcess;
- if (allowCallingUidStartActivity) {
+ if (appSwitchAllowedOrFg && callingUidHasAnyVisibleWindow) {
return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false,
- "callingUidHasAnyVisibleWindow = "
- + callingUid
- + ", isCallingUidPersistentSystemProcess = "
- + isCallingUidPersistentSystemProcess);
+ /*background*/ false, "callingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) {
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ /*background*/ false, "callingUid has non-app visible window");
+ }
+
+ if (isCallingUidPersistentSystemProcess) {
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ false, "callingUid is persistent system process");
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
@@ -806,13 +816,29 @@ public class BackgroundActivityStartController {
"realCallingUid has BAL permission.");
}
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
- if (state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false,
- "realCallingUid has visible (non-toast) window.");
+ // Normal apps with visible app window will be allowed to start activity if app switching
+ // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+ final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
+ || state.mAppSwitchState == APP_SWITCH_FG_ONLY;
+ if (Flags.balImproveRealCallerVisibilityCheck()) {
+ if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has non-app visible window");
+ }
+ } else {
+ // don't abort if the realCallingUid has a visible window
+ // TODO(b/171459802): We should check appSwitchAllowed also
+ if (state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
+ "realCallingUid has visible (non-toast) window.");
+ }
}
+
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2fabb0ea686a..7ce9de4e1c24 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -83,6 +83,7 @@ public abstract class Dimmer {
/**
* Mark all dims as pending completion on the next call to {@link #updateDims}
*
+ * Called before iterating on mHost's children, first step of dimming.
* This is intended for us by the host container, to be called at the beginning of
* {@link WindowContainer#prepareSurfaces}. After calling this, the container should
* chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
@@ -100,8 +101,7 @@ public abstract class Dimmer {
/**
* Call after invoking {@link WindowContainer#prepareSurfaces} on children as
- * described in {@link #resetDimStates}. The dim bounds returned by {@link #resetDimStates}
- * should be set before calling this method.
+ * described in {@link #resetDimStates}.
*
* @param t A transaction in which to update the dims.
* @return true if any Dims were updated.
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
new file mode 100644
index 000000000000..e91857f1da82
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
+import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+
+/**
+ * Contains the information relative to the changes to apply to the dim layer
+ */
+public class DimmerAnimationHelper {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationHelper" : TAG_WM;
+ private static final int DEFAULT_DIM_ANIM_DURATION_MS = 200;
+
+ /**
+ * Contains the requested changes
+ */
+ static class Change {
+ private float mAlpha = -1f;
+ private int mBlurRadius = -1;
+ private WindowContainer mDimmingContainer = null;
+ private int mRelativeLayer = -1;
+ private static final float EPSILON = 0.0001f;
+
+ Change() {}
+
+ Change(Change other) {
+ mAlpha = other.mAlpha;
+ mBlurRadius = other.mBlurRadius;
+ mDimmingContainer = other.mDimmingContainer;
+ mRelativeLayer = other.mRelativeLayer;
+ }
+
+ // Same alpha and blur
+ boolean hasSameVisualProperties(Change other) {
+ return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius;
+ }
+
+ boolean hasSameDimmingContainer(Change other) {
+ return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer;
+ }
+
+ void inheritPropertiesFromAnimation(AnimationSpec anim) {
+ mAlpha = anim.mCurrentAlpha;
+ mBlurRadius = anim.mCurrentBlur;
+ }
+
+ @Override
+ public String toString() {
+ return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
+ + mDimmingContainer + ", relativePosition=" + mRelativeLayer;
+ }
+ }
+
+ private Change mCurrentProperties = new Change();
+ private Change mRequestedProperties = new Change();
+ private AnimationSpec mAlphaAnimationSpec;
+
+ private final AnimationAdapterFactory mAnimationAdapterFactory;
+ private AnimationAdapter mLocalAnimationAdapter;
+
+ DimmerAnimationHelper(AnimationAdapterFactory animationFactory) {
+ mAnimationAdapterFactory = animationFactory;
+ }
+
+ void setExitParameters() {
+ setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */);
+ setRequestedAppearance(0f /* alpha */, 0 /* blur */);
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+ mRequestedProperties.mDimmingContainer = relativeParent;
+ mRequestedProperties.mRelativeLayer = relativeLayer;
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedAppearance(float alpha, int blurRadius) {
+ mRequestedProperties.mAlpha = alpha;
+ mRequestedProperties.mBlurRadius = blurRadius;
+ }
+
+ /**
+ * Commit the last changes we received. Called after
+ * {@link Change#setExitParameters()},
+ * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedAppearance(float, int)}
+ */
+ void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ if (mRequestedProperties.mDimmingContainer == null) {
+ Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ + "call adjustRelativeLayer?");
+ return;
+ }
+ if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
+ Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ + "does not have a surface");
+ dim.remove(t);
+ return;
+ }
+
+ dim.ensureVisible(t);
+ relativeReparent(dim.mDimSurface,
+ mRequestedProperties.mDimmingContainer.getSurfaceControl(),
+ mRequestedProperties.mRelativeLayer, t);
+
+ if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) {
+ stopCurrentAnimation(dim.mDimSurface);
+
+ if (dim.mSkipAnimation
+ // If the container doesn't change but requests a dim change, then it is
+ // directly providing us the animated values
+ || (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties)
+ && dim.isDimming())) {
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "%s skipping animation and directly setting alpha=%f, blur=%d",
+ dim, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius);
+ setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius, t);
+ dim.mSkipAnimation = false;
+ } else {
+ startAnimation(t, dim);
+ }
+
+ } else if (!dim.isDimming()) {
+ // We are not dimming, so we tried the exit animation but the alpha is already 0,
+ // therefore, let's just remove this surface
+ dim.remove(t);
+ }
+ mCurrentProperties = new Change(mRequestedProperties);
+ }
+
+ private void startAnimation(
+ SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
+ mAlphaAnimationSpec = getRequestedAnimationSpec();
+ mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
+ dim.mHostContainer.mWmService.mSurfaceAnimationRunner);
+
+ float targetAlpha = mRequestedProperties.mAlpha;
+ int targetBlur = mRequestedProperties.mBlurRadius;
+
+ mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
+ ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
+ setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t);
+ if (targetAlpha == 0f && !dim.isDimming()) {
+ dim.remove(t);
+ }
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ });
+ }
+
+ private boolean isAnimating() {
+ return mAlphaAnimationSpec != null;
+ }
+
+ void stopCurrentAnimation(SurfaceControl surface) {
+ if (mLocalAnimationAdapter != null && isAnimating()) {
+ // Save the current animation progress and cancel the animation
+ mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec);
+ mLocalAnimationAdapter.onAnimationCancelled(surface);
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ }
+ }
+
+ private AnimationSpec getRequestedAnimationSpec() {
+ final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
+ final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
+ long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
+ * Math.abs(mRequestedProperties.mAlpha - startAlpha));
+
+ final AnimationSpec spec = new AnimationSpec(
+ new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha),
+ new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius),
+ duration
+ );
+ ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec);
+ return spec;
+ }
+
+ /**
+ * Change the relative parent of this dim layer
+ */
+ void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent,
+ int relativePosition, SurfaceControl.Transaction t) {
+ try {
+ t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
+ } catch (NullPointerException e) {
+ Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
+ }
+ }
+
+ void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) {
+ try {
+ t.setAlpha(sc, alpha);
+ t.setBackgroundBlurRadius(sc, blur);
+ } catch (NullPointerException e) {
+ Log.w(TAG , "Tried to change look of dim " + sc + " after remove", e);
+ }
+ }
+
+ private long getDimDuration(WindowContainer container) {
+ // Use the same duration as the animation on the WindowContainer
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+
+ /**
+ * Collects the animation specifics
+ */
+ static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationSpec" : TAG_WM;
+
+ static class AnimationExtremes<T> {
+ final T mStartValue;
+ final T mFinishValue;
+
+ AnimationExtremes(T fromValue, T toValue) {
+ mStartValue = fromValue;
+ mFinishValue = toValue;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + mStartValue + "->" + mFinishValue + "]";
+ }
+ }
+
+ private final long mDuration;
+ private final AnimationSpec.AnimationExtremes<Float> mAlpha;
+ private final AnimationSpec.AnimationExtremes<Integer> mBlur;
+
+ float mCurrentAlpha = 0;
+ int mCurrentBlur = 0;
+ boolean mStarted = false;
+
+ AnimationSpec(AnimationSpec.AnimationExtremes<Float> alpha,
+ AnimationSpec.AnimationExtremes<Integer> blur, long duration) {
+ mAlpha = alpha;
+ mBlur = blur;
+ mDuration = duration;
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+ if (!mStarted) {
+ // The first frame would end up in the sync transaction, and since this could be
+ // applied after the animation transaction, we avoid putting visible changes here.
+ // The initial state of the animation matches the current state of the dim anyway.
+ mStarted = true;
+ return;
+ }
+ final float fraction = getFraction(currentPlayTime);
+ mCurrentAlpha =
+ fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
+ mCurrentBlur =
+ (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
+ if (sc.isValid()) {
+ t.setAlpha(sc, mCurrentAlpha);
+ t.setBackgroundBlurRadius(sc, mCurrentBlur);
+ } else {
+ Log.w(TAG, "Dimmer#AnimationSpec tried to access " + sc + " after release");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Animation spec: alpha=" + mAlpha + ", blur=" + mBlur;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
+ pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
+ pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
+ pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
+ pw.print(" duration="); pw.println(mDuration);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ALPHA);
+ proto.write(FROM, mAlpha.mStartValue);
+ proto.write(TO, mAlpha.mFinishValue);
+ proto.write(DURATION_MS, mDuration);
+ proto.end(token);
+ }
+ }
+
+ static class AnimationAdapterFactory {
+ public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
+ SurfaceAnimationRunner runner) {
+ return new LocalAnimationAdapter(alphaAnimationSpec, runner);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 07cbd58744cb..50376fed2005 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1153,12 +1153,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mDisplay = display;
mDisplayId = display.getDisplayId();
mCurrentUniqueDisplayId = display.getUniqueId();
- mDisplayUpdater = new ImmediateDisplayUpdater(this);
mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
mWallpaperController = new WallpaperController(mWmService, this);
mWallpaperController.resetLargestDisplay(display);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
+ mDisplayUpdater = new ImmediateDisplayUpdater(this);
mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
* mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -1648,7 +1648,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
? new Transition.ReadyCondition("displayConfig", this) : null;
if (displayConfig != null) {
mTransitionController.waitFor(displayConfig);
- } else if (mTransitionController.isShellTransitionsEnabled()) {
+ } else if (mTransitionController.isShellTransitionsEnabled() && mLastHasContent) {
Slog.e(TAG, "Display reconfigured outside of a transition: " + this);
}
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
diff --git a/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java b/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
index 72e8fcb05bb9..4af9013d7f4a 100644
--- a/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
@@ -30,6 +30,7 @@ public class ImmediateDisplayUpdater implements DisplayUpdater {
public ImmediateDisplayUpdater(@NonNull DisplayContent displayContent) {
mDisplayContent = displayContent;
+ mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
}
@Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2bd732774ed3..522e7d205a00 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -627,7 +627,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void refreshSecureSurfaceState() {
forAllWindows((w) -> {
if (w.mHasSurface) {
- w.mWinAnimator.setSecureLocked(w.isSecureLocked());
+ w.setSecureLocked(w.isSecureLocked());
}
}, true /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index 2549bbf70e9f..b5d94a2efbdf 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -17,397 +17,212 @@
package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
-import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.AnimationSpecProto.ALPHA;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.graphics.Rect;
import android.util.Log;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import java.io.PrintWriter;
-
class SmoothDimmer extends Dimmer {
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
- private static final float EPSILON = 0.0001f;
- // This is in milliseconds.
- private static final int DEFAULT_DIM_ANIM_DURATION = 200;
DimState mDimState;
- private WindowContainer mLastRequestedDimContainer;
- private final AnimationAdapterFactory mAnimationAdapterFactory;
+ final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
+ /**
+ * Controls the dim behaviour
+ */
@VisibleForTesting
class DimState {
- /**
- * The layer where property changes should be invoked on.
- */
- SurfaceControl mDimLayer;
- boolean mDimming;
- boolean mIsVisible;
-
+ /** Related objects */
+ SurfaceControl mDimSurface;
+ final WindowContainer mHostContainer;
+ // The last container to request to dim
+ private WindowContainer mLastRequestedDimContainer;
+ /** Animation */
+ private final DimmerAnimationHelper mAnimationHelper;
+ boolean mSkipAnimation = false;
+ // Determines whether the dim layer should animate before destroying.
+ boolean mAnimateExit = true;
+ /** Surface visibility and bounds */
+ private boolean mIsVisible = false;
// TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
final Rect mDimBounds = new Rect();
- /**
- * Determines whether the dim layer should animate before destroying.
- */
- boolean mAnimateExit = true;
-
- /**
- * Used for Dims not associated with a WindowContainer.
- * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
- * lifecycle.
- */
- boolean mDontReset;
-
- Change mCurrentProperties;
- Change mRequestedProperties;
- private AnimationSpec mAlphaAnimationSpec;
- private AnimationAdapter mLocalAnimationAdapter;
-
- static class Change {
- private float mAlpha = -1f;
- private int mBlurRadius = -1;
- private WindowContainer mDimmingContainer = null;
- private int mRelativeLayer = -1;
- private boolean mSkipAnimation = false;
-
- Change() {}
-
- Change(Change other) {
- mAlpha = other.mAlpha;
- mBlurRadius = other.mBlurRadius;
- mDimmingContainer = other.mDimmingContainer;
- mRelativeLayer = other.mRelativeLayer;
- }
-
- @Override
- public String toString() {
- return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
- + mDimmingContainer + ", relativePosition=" + mRelativeLayer
- + ", skipAnimation=" + mSkipAnimation;
+ DimState() {
+ mHostContainer = mHost;
+ mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory);
+ try {
+ mDimSurface = makeDimLayer();
+ } catch (Surface.OutOfResourcesException e) {
+ Log.w(TAG, "OutOfResourcesException creating dim surface");
}
}
- DimState(SurfaceControl dimLayer) {
- mDimLayer = dimLayer;
- mDimming = true;
- mCurrentProperties = new Change();
- mRequestedProperties = new Change();
- }
-
- void setExitParameters(WindowContainer container) {
- setRequestedRelativeParent(container, -1 /* relativeLayer */);
- setRequestedAppearance(0f /* alpha */, 0 /* blur */);
+ void ensureVisible(SurfaceControl.Transaction t) {
+ if (!mIsVisible) {
+ t.show(mDimSurface);
+ t.setAlpha(mDimSurface, 0f);
+ mIsVisible = true;
+ }
}
- // Sets a requested change without applying it immediately
- void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
- mRequestedProperties.mDimmingContainer = relativeParent;
- mRequestedProperties.mRelativeLayer = relativeLayer;
+ void adjustSurfaceLayout(SurfaceControl.Transaction t) {
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
+ t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
}
- // Sets a requested change without applying it immediately
- void setRequestedAppearance(float alpha, int blurRadius) {
- mRequestedProperties.mAlpha = alpha;
- mRequestedProperties.mBlurRadius = blurRadius;
+ /**
+ * Set the parameters to prepare the dim to change its appearance
+ */
+ void prepareLookChange(float alpha, int blurRadius) {
+ mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
}
/**
- * Commit the last changes we received. Called after
- * {@link Change#setExitParameters(WindowContainer)},
- * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
- * {@link Change#setRequestedAppearance(float, int)}
+ * Prepare the dim for the exit animation
*/
- void applyChanges(SurfaceControl.Transaction t) {
- if (mRequestedProperties.mDimmingContainer == null) {
- Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
- + "call adjustRelativeLayer?");
- return;
- }
- if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
- Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
- + "does not have a surface");
- return;
- }
- if (!mDimState.mIsVisible) {
- mDimState.mIsVisible = true;
- t.show(mDimState.mDimLayer);
+ void exit(SurfaceControl.Transaction t) {
+ if (!mAnimateExit) {
+ remove(t);
+ } else {
+ mAnimationHelper.setExitParameters();
+ setReady(t);
}
- t.setRelativeLayer(mDimLayer,
- mRequestedProperties.mDimmingContainer.getSurfaceControl(),
- mRequestedProperties.mRelativeLayer);
+ }
- if (aspectChanged()) {
- if (isAnimating()) {
- mLocalAnimationAdapter.onAnimationCancelled(mDimLayer);
- }
- if (mRequestedProperties.mSkipAnimation
- || (!dimmingContainerChanged() && mDimming)) {
- // If the dimming container has not changed, then it is running its own
- // animation, thus we can directly set the values we get requested, unless it's
- // the exiting animation
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- mDimLayer, mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- mRequestedProperties.mSkipAnimation = false;
- } else {
- startAnimation(t);
- }
+ void remove(SurfaceControl.Transaction t) {
+ mAnimationHelper.stopCurrentAnimation(mDimSurface);
+ if (mDimSurface.isValid()) {
+ t.remove(mDimSurface);
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "Removing dim surface %s on transaction %s", this, t);
+ } else {
+ Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
}
- mCurrentProperties = new Change(mRequestedProperties);
}
- private void startAnimation(SurfaceControl.Transaction t) {
- mAlphaAnimationSpec = getRequestedAnimationSpec(mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
- mHost.mWmService.mSurfaceAnimationRunner);
-
- mLocalAnimationAdapter.startAnimation(mDimLayer, t,
- ANIMATION_TYPE_DIMMER, (type, animator) -> {
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- if (mRequestedProperties.mAlpha == 0f && !mDimming) {
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Removing dim surface %s on transaction %s", mDimLayer, t);
- t.remove(mDimLayer);
- }
- mLocalAnimationAdapter = null;
- mAlphaAnimationSpec = null;
- });
+ @Override
+ public String toString() {
+ return "SmoothDimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
}
- private boolean isAnimating() {
- return mAlphaAnimationSpec != null;
+ /**
+ * Set the parameters to prepare the dim to be relative parented to the dimming container
+ */
+ void prepareReparent(WindowContainer relativeParent, int relativeLayer) {
+ mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
}
- private boolean aspectChanged() {
- return Math.abs(mRequestedProperties.mAlpha - mCurrentProperties.mAlpha) > EPSILON
- || mRequestedProperties.mBlurRadius != mCurrentProperties.mBlurRadius;
+ /**
+ * Call when all the changes have been requested to have them applied
+ * @param t The transaction in which to apply the changes
+ */
+ void setReady(SurfaceControl.Transaction t) {
+ mAnimationHelper.applyChanges(t, this);
}
- private boolean dimmingContainerChanged() {
- return mRequestedProperties.mDimmingContainer != mCurrentProperties.mDimmingContainer;
+ /**
+ * Whether anyone is currently requesting the dim
+ */
+ boolean isDimming() {
+ return mLastRequestedDimContainer != null;
}
- private AnimationSpec getRequestedAnimationSpec(float targetAlpha, int targetBlur) {
- final float startAlpha;
- final int startBlur;
- if (mAlphaAnimationSpec != null) {
- startAlpha = mAlphaAnimationSpec.mCurrentAlpha;
- startBlur = mAlphaAnimationSpec.mCurrentBlur;
- } else {
- startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
- startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
- }
- long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
- * Math.abs(targetAlpha - startAlpha));
-
- ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on dim layer %s, requested by %s, "
- + "alpha: %f -> %f, blur: %d -> %d",
- mDimLayer, mRequestedProperties.mDimmingContainer, startAlpha, targetAlpha,
- startBlur, targetBlur);
- return new AnimationSpec(
- new AnimationExtremes<>(startAlpha, targetAlpha),
- new AnimationExtremes<>(startBlur, targetBlur),
- duration
- );
+ private SurfaceControl makeDimLayer() {
+ return mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer()
+ .setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("DimLayer.makeDimLayer")
+ .build();
}
}
protected SmoothDimmer(WindowContainer host) {
- this(host, new AnimationAdapterFactory());
+ this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
}
@VisibleForTesting
- SmoothDimmer(WindowContainer host, AnimationAdapterFactory animationFactory) {
+ SmoothDimmer(WindowContainer host,
+ DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
super(host);
mAnimationAdapterFactory = animationFactory;
}
- private DimState obtainDimState(WindowContainer container) {
- if (mDimState == null) {
- try {
- final SurfaceControl ctl = makeDimLayer();
- mDimState = new DimState(ctl);
- } catch (Surface.OutOfResourcesException e) {
- Log.w(TAG, "OutOfResourcesException creating dim surface");
- }
- }
-
- mLastRequestedDimContainer = container;
- return mDimState;
- }
-
- private SurfaceControl makeDimLayer() {
- return mHost.makeChildSurface(null)
- .setParent(mHost.getSurfaceControl())
- .setColorLayer()
- .setName("Dim Layer for - " + mHost.getName())
- .setCallsite("Dimmer.makeDimLayer")
- .build();
- }
-
- @Override
- SurfaceControl getDimLayer() {
- return mDimState != null ? mDimState.mDimLayer : null;
- }
-
@Override
void resetDimStates() {
- if (mDimState == null) {
- return;
- }
- if (!mDimState.mDontReset) {
- mDimState.mDimming = false;
- }
- }
-
- @Override
- Rect getDimBounds() {
- return mDimState != null ? mDimState.mDimBounds : null;
- }
-
- @Override
- void dontAnimateExit() {
if (mDimState != null) {
- mDimState.mAnimateExit = false;
+ mDimState.mLastRequestedDimContainer = null;
}
}
@Override
protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
final DimState d = obtainDimState(container);
- mDimState.setRequestedAppearance(alpha, blurRadius);
- d.mDimming = true;
+ d.prepareLookChange(alpha, blurRadius);
}
@Override
protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
if (mDimState != null) {
- mDimState.setRequestedRelativeParent(container, relativeLayer);
+ mDimState.prepareReparent(container, relativeLayer);
}
}
+ @Override
boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
return false;
}
-
- if (!mDimState.mDimming) {
- // No one is dimming anymore, fade out dim and remove
- if (!mDimState.mAnimateExit) {
- if (mDimState.mDimLayer.isValid()) {
- t.remove(mDimState.mDimLayer);
- }
- } else {
- mDimState.setExitParameters(
- mDimState.mRequestedProperties.mDimmingContainer);
- mDimState.applyChanges(t);
- }
+ if (!mDimState.isDimming()) {
+ // No one is dimming, fade out and remove the dim
+ mDimState.exit(t);
mDimState = null;
return false;
+ } else {
+ // Someone is dimming, show the requested changes
+ mDimState.adjustSurfaceLayout(t);
+ final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
+ if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
+ && ws.mActivityRecord.mStartingData != null) {
+ // Skip enter animation while starting window is on top of its activity
+ mDimState.mSkipAnimation = true;
+ }
+ mDimState.setReady(t);
+ return true;
}
- final Rect bounds = mDimState.mDimBounds;
- // TODO: Once we use geometry from hierarchy this falls away.
- t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
- t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
- // Skip enter animation while starting window is on top of its activity
- final WindowState ws = mLastRequestedDimContainer.asWindowState();
- if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
- && ws.mActivityRecord.mStartingData != null) {
- mDimState.mRequestedProperties.mSkipAnimation = true;
- }
- mDimState.applyChanges(t);
- return true;
- }
-
- private long getDimDuration(WindowContainer container) {
- // Use the same duration as the animation on the WindowContainer
- AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
- final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
- return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
- : animationAdapter.getDurationHint();
}
- private static class AnimationExtremes<T> {
- final T mStartValue;
- final T mFinishValue;
-
- AnimationExtremes(T fromValue, T toValue) {
- mStartValue = fromValue;
- mFinishValue = toValue;
+ private DimState obtainDimState(WindowContainer container) {
+ if (mDimState == null) {
+ mDimState = new DimState();
}
+ mDimState.mLastRequestedDimContainer = container;
+ return mDimState;
}
- private static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
- private final long mDuration;
- private final AnimationExtremes<Float> mAlpha;
- private final AnimationExtremes<Integer> mBlur;
-
- float mCurrentAlpha = 0;
- int mCurrentBlur = 0;
-
- AnimationSpec(AnimationExtremes<Float> alpha,
- AnimationExtremes<Integer> blur, long duration) {
- mAlpha = alpha;
- mBlur = blur;
- mDuration = duration;
- }
-
- @Override
- public long getDuration() {
- return mDuration;
- }
-
- @Override
- public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
- final float fraction = getFraction(currentPlayTime);
- mCurrentAlpha =
- fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
- mCurrentBlur =
- (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
- t.setAlpha(sc, mCurrentAlpha);
- t.setBackgroundBlurRadius(sc, mCurrentBlur);
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
- pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
- pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
- pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
- pw.print(" duration="); pw.println(mDuration);
- }
-
- @Override
- public void dumpDebugInner(ProtoOutputStream proto) {
- final long token = proto.start(ALPHA);
- proto.write(FROM, mAlpha.mStartValue);
- proto.write(TO, mAlpha.mFinishValue);
- proto.write(DURATION_MS, mDuration);
- proto.end(token);
- }
+ @Override
+ @VisibleForTesting
+ SurfaceControl getDimLayer() {
+ return mDimState != null ? mDimState.mDimSurface : null;
}
- static class AnimationAdapterFactory {
+ @Override
+ Rect getDimBounds() {
+ return mDimState != null ? mDimState.mDimBounds : null;
+ }
- public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
- SurfaceAnimationRunner runner) {
- return new LocalAnimationAdapter(alphaAnimationSpec, runner);
+ @Override
+ void dontAnimateExit() {
+ if (mDimState != null) {
+ mDimState.mAnimateExit = false;
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 73755121daf8..5f082124dbcb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -422,6 +422,11 @@ class Task extends TaskFragment {
// TODO: remove this once the recents animation is moved to the Shell
SurfaceControl mLastRecentsAnimationOverlay;
+ // A surface that is used by TaskFragmentOrganizer to place content on top of own activities and
+ // trusted TaskFragments.
+ @Nullable
+ DecorSurfaceContainer mDecorSurfaceContainer;
+
static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
@@ -1540,6 +1545,11 @@ class Task extends TaskFragment {
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
+ if (mDecorSurfaceContainer != null && r == mDecorSurfaceContainer.mOwnerTaskFragment) {
+ // Remove the decor surface if the owner TaskFragment is removed;
+ removeDecorSurface();
+ }
+
if (hasChild()) {
updateEffectiveIntent();
@@ -2638,6 +2648,9 @@ class Task extends TaskFragment {
}
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
+ if (mDecorSurfaceContainer != null) {
+ mDecorSurfaceContainer.release();
+ }
super.removeImmediately();
mRemoving = false;
@@ -3644,7 +3657,8 @@ class Task extends TaskFragment {
*/
TaskFragmentParentInfo getTaskFragmentParentInfo() {
return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
- shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity());
+ shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(),
+ getDecorSurface());
}
@Override
@@ -3666,6 +3680,62 @@ class Task extends TaskFragment {
}
}
+ @Override
+ void assignChildLayers(@NonNull SurfaceControl.Transaction t) {
+ int layer = 0;
+ boolean decorSurfacePlaced = false;
+
+ // We use two passes as a way to promote children which
+ // need Z-boosting to the end of the list.
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ wc.assignChildLayers(t);
+ if (!wc.needsZBoost()) {
+ // Place the decor surface under any untrusted content.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+ && shouldPlaceDecorSurfaceBelowContainer(wc)) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+ wc.assignLayer(t, layer++);
+
+ // Place the decor surface just above the owner TaskFragment.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced
+ && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+ }
+ }
+
+ // If not placed yet, the decor surface should be on top of all non-boosted children.
+ if (mDecorSurfaceContainer != null && !decorSurfacePlaced) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ if (wc.needsZBoost()) {
+ wc.assignLayer(t, layer++);
+ }
+ }
+ if (mOverlayHost != null) {
+ mOverlayHost.setLayer(t, layer++);
+ }
+ }
+
+ boolean shouldPlaceDecorSurfaceBelowContainer(@NonNull WindowContainer wc) {
+ boolean isOwnActivity =
+ wc.asActivityRecord() != null
+ && wc.asActivityRecord().isUid(effectiveUid);
+ boolean isTrustedTaskFragment =
+ wc.asTaskFragment() != null
+ && wc.asTaskFragment().isEmbedded()
+ && wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode();
+ return !isOwnActivity && !isTrustedTaskFragment;
+ }
+
boolean isTaskId(int taskId) {
return mTaskId == taskId;
}
@@ -6177,6 +6247,7 @@ class Task extends TaskFragment {
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
+ && display != null
&& display.isDefaultDisplay) {
return false;
}
@@ -6672,4 +6743,77 @@ class Task extends TaskFragment {
mOverlayHost.dispatchInsetsChanged(s, mTmpRect);
}
}
+
+ /**
+ * Associates the decor surface with the given TF, or create one if there
+ * isn't one in the Task yet. The surface will be removed with the TF,
+ * and become invisible if the TF is invisible. */
+ void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment) {
+ if (mDecorSurfaceContainer != null) {
+ mDecorSurfaceContainer.mOwnerTaskFragment = taskFragment;
+ } else {
+ mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment);
+ assignChildLayers();
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
+ }
+
+ void removeDecorSurface() {
+ if (mDecorSurfaceContainer == null) {
+ return;
+ }
+ mDecorSurfaceContainer.release();
+ mDecorSurfaceContainer = null;
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
+
+ @Nullable SurfaceControl getDecorSurface() {
+ return mDecorSurfaceContainer != null ? mDecorSurfaceContainer.mDecorSurface : null;
+ }
+
+ /**
+ * A decor surface that is requested by a {@code TaskFragmentOrganizer} which will be placed
+ * below children windows except for own Activities and TaskFragment in fully trusted mode.
+ */
+ @VisibleForTesting
+ class DecorSurfaceContainer {
+ @VisibleForTesting
+ @NonNull final SurfaceControl mContainerSurface;
+
+ @VisibleForTesting
+ @NonNull final SurfaceControl mDecorSurface;
+
+ // The TaskFragment that requested the decor surface. If it is destroyed, the decor surface
+ // is also released.
+ @VisibleForTesting
+ @NonNull TaskFragment mOwnerTaskFragment;
+
+ private DecorSurfaceContainer(@NonNull TaskFragment initialOwner) {
+ mOwnerTaskFragment = initialOwner;
+ mContainerSurface = makeSurface().setContainerLayer()
+ .setParent(mSurfaceControl)
+ .setName(mSurfaceControl + " - decor surface container")
+ .setEffectLayer()
+ .setHidden(false)
+ .setCallsite("Task.DecorSurfaceContainer")
+ .build();
+
+ mDecorSurface = makeSurface()
+ .setParent(mContainerSurface)
+ .setName(mSurfaceControl + " - decor surface")
+ .setHidden(false)
+ .setCallsite("Task.DecorSurfaceContainer")
+ .build();
+ }
+
+ private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) {
+ t.setLayer(mContainerSurface, layer);
+ t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible());
+ }
+
+ private void release() {
+ mDecorSurface.release();
+ mContainerSurface.release();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 8bc461f05387..39b4480a7da0 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -316,7 +316,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** Organizer that organizing this TaskFragment. */
@Nullable
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
- private int mTaskFragmentOrganizerUid = INVALID_UID;
+ @VisibleForTesting
+ int mTaskFragmentOrganizerUid = INVALID_UID;
private @Nullable String mTaskFragmentOrganizerProcessName;
/** Client assigned unique token for this TaskFragment if this is created by an organizer. */
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index e7a1cf106a44..707f9fc9ea5f 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -762,6 +762,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTask(task)
.build());
}
+ // Make sure the parent info changed event will be dispatched if there are no other changes.
+ mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index caa57bb032ca..e5604eca3df0 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1737,8 +1737,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
- mLogger.logOnSendAsync(mController.mLoggerHandler);
if (mLogger.mInfo != null) {
+ mLogger.logOnSendAsync(mController.mLoggerHandler);
mController.mTransitionTracer.logSentTransition(this, mTargets);
}
}
@@ -1756,6 +1756,27 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
/**
+ * Checks if the transition contains order changes.
+ *
+ * This is a shallow check that doesn't account for collection in parallel, unlike
+ * {@code collectOrderChanges}
+ */
+ boolean hasOrderChanges() {
+ ArrayList<Task> onTopTasks = new ArrayList<>();
+ // Iterate over target displays to get up to date on top tasks.
+ // Cannot use `mOnTopTasksAtReady` as it's not populated before the `applyReady` is called.
+ for (DisplayContent dc : mTargetDisplays) {
+ addOnTopTasks(dc, onTopTasks);
+ }
+ for (Task task : onTopTasks) {
+ if (!mOnTopTasksStart.contains(task)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Collect tasks which moved-to-top as part of this transition. This also updates the
* controller's latest-reported when relevant.
*
@@ -3305,7 +3326,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
*/
void addGroup(WindowContainer wc) {
if (mReadyGroups.containsKey(wc)) {
- Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
return;
}
mReadyGroups.put(wc, false);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bacfda5fc528..e648d6417e88 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -792,6 +792,12 @@ class TransitionController {
mCollectingTransition.recordTaskOrder(wc);
}
+ /** @see Transition#hasOrderChanges */
+ boolean hasOrderChanges() {
+ if (mCollectingTransition == null) return false;
+ return mCollectingTransition.hasOrderChanges();
+ }
+
/**
* Collects the window containers which need to be synced with the changing display area into
* the current collecting transition.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 575ae69be12b..dd2b48bb5a3d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2327,8 +2327,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& win.hasWallpaper();
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
- if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
- winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
+ if ((flagChanges & FLAG_SECURE) != 0) {
+ win.setSecureLocked(win.isSecureLocked());
}
final boolean wasVisible = win.isVisible();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 208df6c768bf..2af656942a2a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -23,7 +23,9 @@ import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1468,6 +1470,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
break;
}
+ case OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE: {
+ final Task task = taskFragment.getTask();
+ task.moveOrCreateDecorSurfaceFor(taskFragment);
+ break;
+ }
+ case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: {
+ final Task task = taskFragment.getTask();
+ task.removeDecorSurface();
+ break;
+ }
}
return effects;
}
@@ -1507,6 +1519,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return false;
}
+ // TODO (b/293654166) remove the decor surface checks once we clear security reviews
+ if ((opType == OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE
+ || opType == OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
+ && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ final Throwable exception = new SecurityException(
+ "Only a system organizer can perform OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE"
+ + " or OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE."
+ );
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ opType, exception);
+ return false;
+ }
+
final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
return secondaryFragmentToken == null
|| validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6d6bcc88e8aa..e1f1f662c5aa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -109,6 +109,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RESIZE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
@@ -177,6 +178,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.secureWindowState;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
@@ -1195,6 +1197,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) {
getPendingTransaction().setTrustedOverlay(mSurfaceControl, true);
}
+ if (secureWindowState()) {
+ getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
+ }
}
void updateTrustedOverlay() {
@@ -6042,4 +6047,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Cancel any draw requests during a sync.
return mPrepareSyncSeqId > 0;
}
+
+ void setSecureLocked(boolean isSecure) {
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName());
+ if (secureWindowState()) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ getPendingTransaction().setSecure(mSurfaceControl, isSecure);
+ } else {
+ if (mWinAnimator.mSurfaceController == null
+ || mWinAnimator.mSurfaceController.mSurfaceControl == null) {
+ return;
+ }
+ getPendingTransaction().setSecure(mWinAnimator.mSurfaceController.mSurfaceControl,
+ isSecure);
+ }
+ if (mDisplayContent != null) {
+ mDisplayContent.refreshImeSecureFlag(getSyncTransaction());
+ }
+ mWmService.scheduleAnimationLocked();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3aac816fcd7a..44cd23d037c6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -44,6 +44,7 @@ import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.window.flags.Flags.secureWindowState;
import android.content.Context;
import android.graphics.PixelFormat;
@@ -286,8 +287,10 @@ class WindowStateAnimator {
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
- if (w.isSecureLocked()) {
- flags |= SurfaceControl.SECURE;
+ if (!secureWindowState()) {
+ if (w.isSecureLocked()) {
+ flags |= SurfaceControl.SECURE;
+ }
}
if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
@@ -488,13 +491,6 @@ class WindowStateAnimator {
mSurfaceController.setOpaque(isOpaque);
}
- void setSecureLocked(boolean isSecure) {
- if (mSurfaceController == null) {
- return;
- }
- mSurfaceController.setSecure(isSecure);
- }
-
void setColorSpaceAgnosticLocked(boolean agnostic) {
if (mSurfaceController == null) {
return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d348491b3d2a..4456a94ef510 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -24,7 +24,6 @@ import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
@@ -152,24 +151,6 @@ class WindowSurfaceController {
mService.scheduleAnimationLocked();
}
- void setSecure(boolean isSecure) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
-
- if (mSurfaceControl == null) {
- return;
- }
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
-
- final SurfaceControl.Transaction t = mAnimator.mWin.getPendingTransaction();
- t.setSecure(mSurfaceControl, isSecure);
-
- final DisplayContent dc = mAnimator.mWin.mDisplayContent;
- if (dc != null) {
- dc.refreshImeSecureFlag(t);
- }
- mService.scheduleAnimationLocked();
- }
-
void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index cca4261795ab..c625b1e1eef7 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -159,6 +159,12 @@
<xs:element type="usiVersion" name="usiVersion">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Maximum screen brightness setting when screen brightness capped in
+ Wear Bedtime mode. This must be a non-negative decimal within the range defined by
+ the first and the last brightness value in screenBrightnessMap. -->
+ <xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -586,42 +592,39 @@
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
- <!-- Sets the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
+ <!-- Sets the brightness mapping of the desired screen brightness to the corresponding
+ lux for the current display -->
+ <xs:element name="luxToBrightnessMapping" type="luxToBrightnessMapping"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
</xs:complexType>
- <!-- Represents the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:complexType name="displayBrightnessMapping">
- <xs:sequence>
- <!-- Sets the list of display brightness points, each representing the desired screen
- brightness in nits to the corresponding lux for the current display
-
- The N entries of this array define N + 1 control points as follows:
- (1-based arrays)
-
- Point 1: (0, nits[1]): currentLux <= 0
- Point 2: (lux[1], nits[2]): 0 < currentLux <= lux[1]
- Point 3: (lux[2], nits[3]): lux[2] < currentLux <= lux[3]
- ...
- Point N+1: (lux[N], nits[N+1]): lux[N] < currentLux
-
- The control points must be strictly increasing. Each control point
- corresponds to an entry in the brightness backlight values arrays.
- For example, if currentLux == lux[1] (first element of the levels array)
- then the brightness will be determined by nits[2] (second element
- of the brightness values array).
- -->
- <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
- minOccurs="1" maxOccurs="unbounded">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
+ <!-- Sets the list of display brightness points, each representing the desired screen brightness
+ in a certain lux environment.
+
+ The first value of each point is the lux value and the second value is the brightness value.
+
+ The first lux value must be 0.
+
+ The control points must be strictly increasing.
+
+ Example: if currentLux == the second lux value in the mapping then the brightness will be
+ determined by the second brightness value in the mapping. Spline interpolation is used
+ to determine the auto-brightness values for lux levels between these control points.
+
+ The brightness values must be non-negative decimals within the range between the first and
+ the last brightness values in screenBrightnessMap.
+
+ This is used in place of config_autoBrightnessLevels and config_autoBrightnessLcdBacklightValues
+ defined in the config XML resource.
+ -->
+ <xs:complexType name="luxToBrightnessMapping">
+ <xs:element name="map" type="nonNegativeFloatToFloatMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:complexType>
<!-- Represents a point in the display brightness mapping, representing the lux level from the
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index f767291b4953..8c8c1230f944 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,14 +7,14 @@ package com.android.server.display.config {
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
- method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public boolean getEnabled();
+ method public final com.android.server.display.config.LuxToBrightnessMapping getLuxToBrightnessMapping();
method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
- method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
method public void setEnabled(boolean);
+ method public final void setLuxToBrightnessMapping(com.android.server.display.config.LuxToBrightnessMapping);
}
public class BlockingZoneConfig {
@@ -78,11 +78,6 @@ package com.android.server.display.config {
method public java.util.List<com.android.server.display.config.Density> getDensity();
}
- public class DisplayBrightnessMapping {
- ctor public DisplayBrightnessMapping();
- method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
- }
-
public class DisplayBrightnessPoint {
ctor public DisplayBrightnessPoint();
method public final java.math.BigInteger getLux();
@@ -110,6 +105,7 @@ package com.android.server.display.config {
method public final com.android.server.display.config.SensorDetails getProxSensor();
method public com.android.server.display.config.DisplayQuirks getQuirks();
method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
+ method public final java.math.BigDecimal getScreenBrightnessCapForWearBedtimeMode();
method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxIdleMillis();
@@ -143,6 +139,7 @@ package com.android.server.display.config {
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
method public void setQuirks(com.android.server.display.config.DisplayQuirks);
method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
+ method public final void setScreenBrightnessCapForWearBedtimeMode(java.math.BigDecimal);
method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
method public final void setScreenBrightnessRampDecreaseMaxIdleMillis(java.math.BigInteger);
@@ -220,6 +217,12 @@ package com.android.server.display.config {
method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap();
}
+ public class LuxToBrightnessMapping {
+ ctor public LuxToBrightnessMapping();
+ method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
+ method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ }
+
public class NitsMap {
ctor public NitsMap();
method public String getInterpolation();
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 1988bb6e6e46..da44aac5826a 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -23,12 +23,14 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.credentials.ui.ProviderData;
import android.credentials.ui.UserSelectionDialogResult;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -94,6 +96,9 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
private final Set<ComponentName> mEnabledProviders;
+ private final RequestSessionDeathRecipient mDeathRecipient =
+ new RequestSessionDeathRecipient();
+
protected PendingIntent mPendingIntent;
@NonNull
@@ -141,11 +146,26 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted,
mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType));
setCancellationListener();
+ if (Flags.clearSessionEnabled()) {
+ setUpClientCallbackListener();
+ }
+ }
+
+ private void setUpClientCallbackListener() {
+ if (mClientCallback != null && mClientCallback instanceof IInterface) {
+ IInterface callback = (IInterface) mClientCallback;
+ try {
+ callback.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+ }
}
private void setCancellationListener() {
mCancellationSignal.setOnCancelListener(
() -> {
+ Slog.d(TAG, "Cancellation invoked from the client - clearing session");
boolean isUiActive = maybeCancelUi();
finishSession(!isUiActive);
}
@@ -168,6 +188,17 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
return false;
}
+ private boolean isUiWaitingForData() {
+ // Technically, the status can also be IN_PROGRESS when the user has made a selection
+ // so this an over estimation, but safe to do so as it is used for cancellation
+ // propagation to the provider in a very narrow time frame. If provider has
+ // already responded, cancellation is not an issue as the cancellation listener
+ // is independent of the service binding.
+ // TODO(b/313512500): Do not propagate cancelation if provider has responded in
+ // query phase.
+ return mCredentialManagerUi.getStatus() == CredentialManagerUi.UiStatus.IN_PROGRESS;
+ }
+
public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
RemoteCredentialService remoteCredentialService);
@@ -373,4 +404,12 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential
return chosenProviderSession != null && chosenProviderSession.mProviderInfo != null
&& chosenProviderSession.mProviderInfo.isPrimary();
}
+
+ private class RequestSessionDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Client binder died - clearing session");
+ finishSession(isUiWaitingForData());
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0a2e80606e96..1919eb33c38c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2112,6 +2112,15 @@ public final class SystemServer implements Dumpable {
networkPolicy.bindConnectivityManager();
t.traceEnd();
+ t.traceBegin("StartSecurityStateManagerService");
+ try {
+ ServiceManager.addService(Context.SECURITY_STATE_SERVICE,
+ new SecurityStateManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting SecurityStateManagerService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartVpnManagerService");
try {
vpnManager = VpnManagerService.create(context);
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index e464038e68d9..487c992bbab4 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,5 +1,3 @@
#Bug component: 137825
-joecastro@google.com
-ntmyren@google.com
-zhanghai@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index f94a0d664a69..8f464d41792d 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -71,7 +71,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
// Not implemented because upgrades are handled automatically.
}
- override fun getNonDefaultUidModes(uid: Int): SparseIntArray {
+ override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray {
return opNameMapToOpSparseArray(getUidModes(uid))
}
@@ -79,7 +79,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
return opNameMapToOpSparseArray(getPackageModes(packageName, userId))
}
- override fun getUidMode(uid: Int, op: Int): Int {
+ override fun getUidMode(uid: Int, persistentDeviceId: String, op: Int): Int {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
@@ -92,7 +92,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
}
- override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean {
+ override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
@@ -150,7 +150,7 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
// and we have our own persistence.
}
- override fun getForegroundOps(uid: Int): SparseBooleanArray {
+ override fun getForegroundOps(uid: Int, persistentDeviceId: String): SparseBooleanArray {
return SparseBooleanArray().apply {
getUidModes(uid)?.forEachIndexed { _, op, mode ->
if (mode == AppOpsManager.MODE_FOREGROUND) {
diff --git a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
deleted file mode 100644
index 0ad418427183..000000000000
--- a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.when;
-
-import android.app.Application;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.MediaRoute2Info;
-import android.os.UserHandle;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowBluetoothAdapter;
-import org.robolectric.shadows.ShadowBluetoothDevice;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(RobolectricTestRunner.class)
-public class AudioPoliciesBluetoothRouteControllerTest {
-
- private static final String DEVICE_ADDRESS_UNKNOWN = ":unknown:ip:address:";
- private static final String DEVICE_ADDRESS_SAMPLE_1 = "30:59:8B:E4:C6:35";
- private static final String DEVICE_ADDRESS_SAMPLE_2 = "0D:0D:A6:FF:8D:B6";
- private static final String DEVICE_ADDRESS_SAMPLE_3 = "2D:9B:0C:C2:6F:78";
- private static final String DEVICE_ADDRESS_SAMPLE_4 = "66:88:F9:2D:A8:1E";
-
- private Context mContext;
-
- private ShadowBluetoothAdapter mShadowBluetoothAdapter;
-
- @Mock
- private BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
-
- @Mock
- private BluetoothProfileMonitor mBluetoothProfileMonitor;
-
- private AudioPoliciesBluetoothRouteController mAudioPoliciesBluetoothRouteController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- Application application = ApplicationProvider.getApplicationContext();
- mContext = application;
-
- BluetoothManager bluetoothManager = (BluetoothManager)
- mContext.getSystemService(Context.BLUETOOTH_SERVICE);
-
- BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
- mShadowBluetoothAdapter = Shadows.shadowOf(bluetoothAdapter);
-
- mAudioPoliciesBluetoothRouteController =
- new AudioPoliciesBluetoothRouteController(mContext, bluetoothAdapter,
- mBluetoothProfileMonitor, mListener) {
- @Override
- boolean isDeviceConnected(BluetoothDevice device) {
- return true;
- }
- };
-
- // Enable A2DP profile.
- when(mBluetoothProfileMonitor.isProfileSupported(eq(BluetoothProfile.A2DP), any()))
- .thenReturn(true);
- mShadowBluetoothAdapter.setProfileConnectionState(BluetoothProfile.A2DP,
- BluetoothProfile.STATE_CONNECTED);
-
- mAudioPoliciesBluetoothRouteController.start(UserHandle.of(0));
- }
-
- @Test
- public void getSelectedRoute_noBluetoothRoutesAvailable_returnsNull() {
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
- }
-
- @Test
- public void selectRoute_noBluetoothRoutesAvailable_returnsFalse() {
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_UNKNOWN)).isFalse();
- }
-
- @Test
- public void selectRoute_noDeviceWithGivenAddress_returnsFalse() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_3);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_2)).isFalse();
- }
-
- @Test
- public void selectRoute_deviceIsInDevicesSet_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_1)).isTrue();
- }
-
- @Test
- public void selectRoute_resetSelectedDevice_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_1);
- assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void selectRoute_noSelectedDevice_returnsTrue() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void getSelectedRoute_updateRouteFailed_returnsNull() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
- mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_3);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
- }
-
- @Test
- public void getSelectedRoute_updateRouteSuccessful_returnsUpdateDevice() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- assertThat(mAudioPoliciesBluetoothRouteController
- .selectRoute(DEVICE_ADDRESS_SAMPLE_4)).isTrue();
-
- MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
- assertThat(selectedRoute.getAddress()).isEqualTo(DEVICE_ADDRESS_SAMPLE_4);
- }
-
- @Test
- public void getSelectedRoute_resetSelectedRoute_returnsNull() {
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
- DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
-
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Device is not null now.
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
- // Rest the device.
- mAudioPoliciesBluetoothRouteController.selectRoute(null);
-
- assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute())
- .isNull();
- }
-
- @Test
- public void getTransferableRoutes_noSelectedRoute_returnsAllBluetoothDevices() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
-
- Set<String> transferableDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
- assertThat(transferableDevices).containsExactlyElementsIn(addresses);
- }
-
- @Test
- public void getTransferableRoutes_hasSelectedRoute_returnsRoutesWithoutSelectedDevice() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- Set<String> transferableDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
- assertThat(transferableDevices).containsExactly(DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2);
- }
-
- @Test
- public void getAllBluetoothRoutes_hasSelectedRoute_returnsAllRoutes() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- Set<String> bluetoothDevices = extractAddressesListFrom(
- mAudioPoliciesBluetoothRouteController.getAllBluetoothRoutes());
- assertThat(bluetoothDevices).containsExactlyElementsIn(addresses);
- }
-
- @Test
- public void updateVolumeForDevice_setVolumeForA2DPTo25_selectedRouteVolumeIsUpdated() {
- String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
- DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
- Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
- mShadowBluetoothAdapter.setBondedDevices(devices);
-
- // Force route controller to update bluetooth devices list.
- sendBluetoothDevicesChangedBroadcast();
- mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
-
- mAudioPoliciesBluetoothRouteController.updateVolumeForDevices(
- AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, 25);
-
- MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
- assertThat(selectedRoute.getVolume()).isEqualTo(25);
- }
-
- private void sendBluetoothDevicesChangedBroadcast() {
- Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
- mContext.sendBroadcast(intent);
- }
-
- private static Set<String> extractAddressesListFrom(Collection<MediaRoute2Info> routes) {
- Set<String> addresses = new HashSet<>();
-
- for (MediaRoute2Info route: routes) {
- addresses.add(route.getAddress());
- }
-
- return addresses;
- }
-
- private static Set<BluetoothDevice> generateFakeBluetoothDevicesSet(String... addresses) {
- Set<BluetoothDevice> devices = new HashSet<>();
-
- for (String address: addresses) {
- devices.add(ShadowBluetoothDevice.newInstance(address));
- }
-
- return devices;
- }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 97e582607133..a2e80f0d9b9b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -104,7 +104,7 @@ public class BrightnessMappingStrategyTest {
468.5f,
};
- private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
+ private static final int[] DISPLAY_LEVELS_INT = {
9,
30,
45,
@@ -118,6 +118,20 @@ public class BrightnessMappingStrategyTest {
255
};
+ private static final float[] DISPLAY_LEVELS = {
+ 0.03f,
+ 0.11f,
+ 0.17f,
+ 0.24f,
+ 0.3f,
+ 0.37f,
+ 0.46f,
+ 0.57f,
+ 0.7f,
+ 0.87f,
+ 1
+ };
+
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
@@ -155,23 +169,23 @@ public class BrightnessMappingStrategyTest {
DisplayWhiteBalanceController mMockDwbc;
@Test
- public void testSimpleStrategyMappingAtControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingAtControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]);
+ PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_INT[i]);
assertEquals(expectedLevel,
simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
}
}
@Test
- public void testSimpleStrategyMappingBetweenControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -179,14 +193,42 @@ public class BrightnessMappingStrategyTest {
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
assertTrue("Desired brightness should be between adjacent control points.",
- backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
- && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+ backlight > DISPLAY_LEVELS_INT[i - 1]
+ && backlight < DISPLAY_LEVELS_INT[i]);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingAtControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]),
+ /* tolerance= */ 0.0001f);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingBetweenControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 1; i < LUX_LEVELS.length; i++) {
+ final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+ final float brightness = simple.getBrightness(lux);
+ assertTrue("Desired brightness should be between adjacent control points.",
+ brightness > DISPLAY_LEVELS[i - 1] && brightness < DISPLAY_LEVELS[i]);
}
}
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -201,14 +243,14 @@ public class BrightnessMappingStrategyTest {
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
- final int n = DISPLAY_LEVELS_BACKLIGHT.length;
+ final int n = DISPLAY_LEVELS_INT.length;
final float expectedBrightness =
- (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
+ (float) DISPLAY_LEVELS_INT[n - 1] / PowerManager.BRIGHTNESS_ON;
assertEquals(expectedBrightness,
strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@@ -322,7 +364,7 @@ public class BrightnessMappingStrategyTest {
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -363,13 +405,13 @@ public class BrightnessMappingStrategyTest {
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra backlight level
final int[] backlight = Arrays.copyOf(
- DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1);
+ DISPLAY_LEVELS_INT, DISPLAY_LEVELS_INT.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
res = createResources(backlight);
ddc = createDdc(DISPLAY_RANGE_NITS,
@@ -410,7 +452,7 @@ public class BrightnessMappingStrategyTest {
LUX_LEVELS, DISPLAY_LEVELS_NITS);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
}
@@ -546,16 +588,24 @@ public class BrightnessMappingStrategyTest {
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(EMPTY_FLOAT_ARRAY);
return mockDdc;
}
private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
float[] luxLevelsFloat, float[] brightnessLevelsNits) {
+ return createDdc(nitsArray, backlightArray, luxLevelsFloat, brightnessLevelsNits,
+ EMPTY_FLOAT_ARRAY);
+ }
+
+ private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
+ float[] luxLevelsFloat, float[] brightnessLevelsNits, float[] brightnessLevels) {
DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
when(mockDdc.getNits()).thenReturn(nitsArray);
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(brightnessLevels);
return mockDdc;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 0bcbeb9b8a85..31d7e88e671b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -47,8 +47,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.Before;
import org.junit.Test;
@@ -92,10 +94,14 @@ public final class DisplayDeviceConfigTest {
@Mock
private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -110,10 +116,6 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{45.32f, 75.43f}, ZERO_DELTA);
// Test thresholds
assertEquals(10, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(),
@@ -606,7 +608,7 @@ public final class DisplayDeviceConfigTest {
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
// Test thresholds
assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -671,6 +673,9 @@ public final class DisplayDeviceConfigTest {
assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+
+ assertEquals(BrightnessSynchronizer.brightnessIntToFloat(35),
+ mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
}
@Test
@@ -716,6 +721,38 @@ public final class DisplayDeviceConfigTest {
assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.04f, ZERO_DELTA);
}
+ @Test
+ public void testBrightnessCapForWearBedtimeMode() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+ assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertArrayEquals(new float[]{0.0f, 80},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.2f, 0.3f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(), SMALL_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels_FeatureFlagOff() throws IOException {
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
+ setupDisplayDeviceConfigFromConfigResourceFile();
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertNull(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels());
+ assertArrayEquals(new float[]{0, 110, 500},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{2, 200, 600},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1037,8 +1074,8 @@ public final class DisplayDeviceConfigTest {
+ "<screenBrightnessRampDecreaseMaxIdleMillis>"
+ "5000"
+ "</screenBrightnessRampDecreaseMaxIdleMillis>\n";
-
}
+
private String getContent() {
return getContent(getValidLuxThrottling(), getValidProxSensor(),
/* includeIdleMode= */ true);
@@ -1089,16 +1126,18 @@ public final class DisplayDeviceConfigTest {
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ (includeIdleMode ? getRampSpeedsIdle() : "")
- + "<displayBrightnessMapping>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>50</lux>\n"
- + "<nits>45.32</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>80</lux>\n"
- + "<nits>75.43</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "</displayBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.2</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>80</first>\n"
+ + "<second>0.3</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ getPowerThrottlingConfig()
+ "<highBrightnessMode enabled=\"true\">\n"
@@ -1355,6 +1394,9 @@ public final class DisplayDeviceConfigTest {
+ "<majorVersion>2</majorVersion>\n"
+ "<minorVersion>0</minorVersion>\n"
+ "</usiVersion>\n"
+ + "<screenBrightnessCapForWearBedtimeMode>"
+ + "0.1"
+ + "</screenBrightnessCapForWearBedtimeMode>"
+ "</displayConfiguration>\n";
}
@@ -1372,7 +1414,7 @@ public final class DisplayDeviceConfigTest {
private void setupDisplayDeviceConfigFromDisplayConfigFile(String content) throws IOException {
Path tempFile = Files.createTempFile("display_config", ".tmp");
Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
- mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext, mFlags);
mDisplayDeviceConfig.initFromFile(tempFile.toFile());
}
@@ -1381,25 +1423,15 @@ public final class DisplayDeviceConfigTest {
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits))
.thenReturn(screenBrightnessNits);
- TypedArray screenBrightnessBacklight = createFloatTypedArray(new
- float[]{0.0f, 120.0f, 255.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessBacklight))
- .thenReturn(screenBrightnessBacklight);
when(mResources.getIntArray(com.android.internal.R.array
.config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
-
TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
float[]{2.0f, 200.0f, 600.0f});
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
.thenReturn(screenBrightnessLevelNits);
- int[] screenBrightnessLevelLux = new int[]{0, 110, 500};
+ int[] screenBrightnessLevelLux = new int[]{110, 500};
when(mResources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels))
.thenReturn(screenBrightnessLevelLux);
@@ -1457,7 +1489,12 @@ public final class DisplayDeviceConfigTest {
R.integer.config_autoBrightnessDarkeningLightDebounce))
.thenReturn(4000);
- mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
+ when(mResources.getInteger(
+ R.integer.config_screenBrightnessCapForWearBedtimeMode))
+ .thenReturn(35);
+
+ mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, /* useConfigXml= */ true,
+ mFlags);
}
private TypedArray createFloatTypedArray(float[] vals) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
index 4fd8f26d91a8..dc6abf1981c0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -57,6 +57,9 @@ public class DisplayDeviceTest {
@Mock
private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private DisplayAdapter mMockDisplayAdapter;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -67,34 +70,39 @@ public class DisplayDeviceTest {
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@@ -102,8 +110,9 @@ public class DisplayDeviceTest {
private static class FakeDisplayDevice extends DisplayDevice {
private final DisplayDeviceInfo mDisplayDeviceInfo;
- FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
- super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+ FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter) {
+ super(displayAdapter, /* displayToken= */ null, /* uniqueId= */ "",
+ InstrumentationRegistry.getInstrumentation().getContext());
mDisplayDeviceInfo = displayDeviceInfo;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 55f56e93fea5..02e3ef4d5f0b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -212,7 +212,8 @@ public class DisplayManagerServiceTest {
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return mMockVirtualDisplayAdapter;
}
@@ -251,7 +252,8 @@ public class DisplayManagerServiceTest {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
@Override
@@ -263,7 +265,7 @@ public class DisplayManagerServiceTest {
@Override
public void destroyDisplay(IBinder displayToken) {
}
- });
+ }, flags);
}
@Override
@@ -329,6 +331,7 @@ public class DisplayManagerServiceTest {
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Mock DisplayManagerFlags mMockFlags;
@@ -788,7 +791,8 @@ public class DisplayManagerServiceTest {
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return null; // return null for the adapter. This should cause a failure.
}
@@ -3194,7 +3198,7 @@ public class DisplayManagerServiceTest {
private DisplayDeviceInfo mDisplayDeviceInfo;
FakeDisplayDevice() {
- super(null, null, "", mContext);
+ super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext);
}
public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 8270845657c6..f36854b1ea78 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1455,8 +1455,8 @@ public class LocalDisplayAdapterTest {
// mMockContext and values will be loaded from mMockResources.
@Override
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay, flags);
}
}
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 43d2b8f741ae..28ec89629df0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -119,8 +119,8 @@ public class LogicalDisplayMapperTest {
@Mock IThermalService mIThermalServiceMock;
@Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
- @Mock
- DisplayManagerFlags mFlagsMock;
+ @Mock DisplayManagerFlags mFlagsMock;
+ @Mock DisplayAdapter mDisplayAdapterMock;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@@ -1075,7 +1075,8 @@ public class LogicalDisplayMapperTest {
private int mState;
TestDisplayDevice() {
- super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
+ super(mDisplayAdapterMock, /* displayToken= */ null,
+ "test_display_" + sUniqueTestDisplayId++, mContextMock);
mInfo = new DisplayDeviceInfo();
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 9f91916a4046..5676a388acff 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -32,9 +32,12 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -51,8 +54,12 @@ public class PersistentDataStoreTest {
private TestInjector mInjector;
private TestLooper mTestLooper;
+ @Mock
+ private DisplayAdapter mDisplayAdapter;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mInjector = new TestInjector();
mTestLooper = new TestLooper();
Handler handler = new Handler(mTestLooper.getLooper());
@@ -62,8 +69,8 @@ public class PersistentDataStoreTest {
@Test
public void testLoadBrightness() {
final String uniqueDisplayId = "test:123";
- final DisplayDevice testDisplayDevice = new DisplayDevice(
- null, null, uniqueDisplayId, null) {
+ final DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -100,7 +107,8 @@ public class PersistentDataStoreTest {
public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
final String uniqueDisplayId = "test:123";
final DisplayDevice testDisplayDevice =
- new DisplayDevice(null, null, uniqueDisplayId, null) {
+ new DisplayDevice(mDisplayAdapter, /* displayToken= */ null, uniqueDisplayId,
+ /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -273,7 +281,8 @@ public class PersistentDataStoreTest {
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -319,7 +328,8 @@ public class PersistentDataStoreTest {
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return false;
@@ -386,7 +396,8 @@ public class PersistentDataStoreTest {
@Test
public void testStoreAndRestoreResolution() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -422,7 +433,8 @@ public class PersistentDataStoreTest {
@Test
public void testStoreAndRestoreRefreshRate() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -455,7 +467,8 @@ public class PersistentDataStoreTest {
@Test
public void testBrightnessInitialisesWithInvalidFloat() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
index b7cbac5a7f18..5c50acb13f30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -72,27 +72,14 @@ public class RefreshRateSettingsUtilsTest {
@Test
public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- assertEquals(120,
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
+ assertEquals(DEFAULT_REFRESH_RATE,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
- }
- @Test
- public void testFindHighestRefreshRate() {
- int displayId = 13;
- when(mDisplayManagerMock.getDisplay(displayId)).thenReturn(mDisplayMock);
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, displayId),
- /* delta= */ 0);
- }
-
- @Test
- public void testFindHighestRefreshRate_DisplayIsNull() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
- assertEquals(DEFAULT_REFRESH_RATE,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
-
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 73e7ba0750e0..c01b15c17483 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -28,6 +28,7 @@ import android.os.IBinder;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
@@ -59,13 +60,17 @@ public class VirtualDisplayAdapterTest {
private VirtualDisplayAdapter mVirtualDisplayAdapter;
+ @Mock
+ private DisplayManagerFlags mFeatureFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mHandler = new TestHandler(null);
mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
- mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory);
+ mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory,
+ mFeatureFlags);
when(mMockCallback.asBinder()).thenReturn(mMockBinder);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index ff2b1f466a5c..9aa6136348b4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -262,7 +262,8 @@ public class BrightnessClamperControllerTest {
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ Context context) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
new file mode 100644
index 000000000000..3458b08245a2
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF;
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class BrightnessWearBedtimeModeClamperTest {
+
+ private static final float BRIGHTNESS_CAP = 0.3f;
+
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private final TestInjector mInjector = new TestInjector();
+ private BrightnessWearBedtimeModeClamper mClamper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext,
+ mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
+ mTestHandler.flush();
+ }
+
+ @Test
+ public void testBrightnessCap() {
+ assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ }
+
+ @Test
+ public void testBedtimeModeOn() {
+ setBedtimeModeEnabled(true);
+ assertTrue(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testBedtimeModeOff() {
+ setBedtimeModeEnabled(false);
+ assertFalse(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testType() {
+ assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+ }
+
+ @Test
+ public void testOnDisplayChanged() {
+ float newBrightnessCap = 0.61f;
+
+ mClamper.onDisplayChanged(() -> newBrightnessCap);
+ mTestHandler.flush();
+
+ assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ private void setBedtimeModeEnabled(boolean enabled) {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
+ enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
+ mInjector.notifyBedtimeModeChanged();
+ mTestHandler.flush();
+ }
+
+ private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector {
+
+ private ContentObserver mObserver;
+
+ @Override
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mObserver = observer;
+ }
+
+ private void notifyBedtimeModeChanged() {
+ if (mObserver != null) {
+ mObserver.dispatchChange(/* selfChange= */ false,
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
+ }
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index d0859232778d..60a0c039dd03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -290,7 +290,6 @@ public class DisplayModeDirectorTest {
};
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
- private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
@@ -1551,12 +1550,9 @@ public class DisplayModeDirectorTest {
public void testPeakRefreshRate_FlagEnabled() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
- float highestRefreshRate1 = 130;
- float highestRefreshRate2 = 132;
- doReturn(highestRefreshRate1).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- doReturn(highestRefreshRate2).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1567,14 +1563,10 @@ public class DisplayModeDirectorTest {
setPeakRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate1);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate2);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ highestRefreshRate);
}
@Test
@@ -1592,54 +1584,19 @@ public class DisplayModeDirectorTest {
setPeakRefreshRate(peakRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ peakRefreshRate);
- }
-
- @Test
- public void testPeakRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setPeakRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ peakRefreshRate);
}
@Test
public void testMinRefreshRate_FlagEnabled() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
- float highestRefreshRate1 = 130;
- float highestRefreshRate2 = 132;
- doReturn(highestRefreshRate1).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- doReturn(highestRefreshRate2).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1650,12 +1607,9 @@ public class DisplayModeDirectorTest {
setMinRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ highestRefreshRate1,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ highestRefreshRate2,
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@@ -1674,44 +1628,13 @@ public class DisplayModeDirectorTest {
setMinRefreshRate(minRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@Test
- public void testMinRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setMinRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- }
-
- @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3406,7 +3329,7 @@ public class DisplayModeDirectorTest {
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private final DisplayInfo mDisplayInfo;
- private final Map<Integer, Display> mDisplays;
+ private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private final DisplayManagerInternal mDisplayManagerInternal;
private final StatusBarManagerInternal mStatusBarManagerInternal;
@@ -3427,8 +3350,7 @@ public class DisplayModeDirectorTest {
mDisplayInfo.defaultModeId = MODE_ID;
mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
800, 600, /* refreshRate= */ 60)};
- mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID),
- DISPLAY_ID_2, createDisplay(DISPLAY_ID_2));
+ mDisplay = createDisplay(DISPLAY_ID);
mDisplayManagerInternal = displayManagerInternal;
mStatusBarManagerInternal = statusBarManagerInternal;
mSensorManagerInternal = sensorManagerInternal;
@@ -3459,12 +3381,12 @@ public class DisplayModeDirectorTest {
@Override
public Display getDisplay(int displayId) {
- return mDisplays.get(displayId);
+ return mDisplay;
}
@Override
public Display[] getDisplays() {
- return mDisplays.values().toArray(new Display[0]);
+ return new Display[] { mDisplay };
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index be29163e7677..1a3a6a388392 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,6 +46,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -61,6 +62,7 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
@@ -128,6 +130,8 @@ public class DeviceIdleControllerTest {
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
+ private ContentResolver mContentResolver;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -332,7 +336,7 @@ public class DeviceIdleControllerTest {
anyString(), any(Executor.class),
any(DeviceConfig.OnPropertiesChangedListener.class)));
doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock
- -> mock(DeviceConfig.Properties.class))
+ -> new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_DEVICE_IDLE).build())
.when(() -> DeviceConfig.getProperties(
anyString(), ArgumentMatchers.<String>any()));
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
@@ -347,6 +351,7 @@ public class DeviceIdleControllerTest {
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
mInjector = new InjectorForTest(getContext());
+ doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
doReturn(mWearModeManagerInternal)
.when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -366,7 +371,8 @@ public class DeviceIdleControllerTest {
mDeviceIdleController.setLightEnabledForTest(true);
// Get the same Constants object that mDeviceIdleController got.
- mConstants = mInjector.getConstants(mDeviceIdleController);
+ mConstants = mInjector.getConstants(mDeviceIdleController,
+ mInjector.getHandler(mDeviceIdleController), mContentResolver);
final ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
ArgumentCaptor.forClass(TelephonyCallback.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 92d1118d0f1e..4f672f81a9cb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -19,6 +19,7 @@ package com.android.server.appop;
import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT;
import static android.app.AppOpsManager._NUM_OP;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -208,8 +209,8 @@ public class AppOpsUpgradeTest {
private void assertSameModes(AppOpsCheckingServiceImpl testService, int op1, int op2) {
for (int uid : testService.getUidsWithNonDefaultModes()) {
assertEquals(
- testService.getUidMode(uid, op1),
- testService.getUidMode(uid, op2)
+ testService.getUidMode(uid, PERSISTENT_DEVICE_ID_DEFAULT, op1),
+ testService.getUidMode(uid, PERSISTENT_DEVICE_ID_DEFAULT, op2)
);
}
for (UserPackage pkg : testService.getPackagesWithNonDefaultModes()) {
@@ -275,7 +276,9 @@ public class AppOpsUpgradeTest {
} else {
expectedMode = previousMode;
}
- int mode = testService.getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_SCHEDULE_EXACT_ALARM);
assertEquals(expectedMode, mode);
}
}
@@ -284,7 +287,9 @@ public class AppOpsUpgradeTest {
int[] unrelatedUidsInFile = {10225, 10178};
for (int uid : unrelatedUidsInFile) {
- int mode = testService.getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_SCHEDULE_EXACT_ALARM);
assertEquals(AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM), mode);
}
}
@@ -331,7 +336,9 @@ public class AppOpsUpgradeTest {
final int uid = UserHandle.getUid(userId, appId);
final int expectedMode = AppOpsManager.opToDefaultMode(OP_USE_FULL_SCREEN_INTENT);
synchronized (testService) {
- int mode = testService.getUidMode(uid, OP_USE_FULL_SCREEN_INTENT);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_USE_FULL_SCREEN_INTENT);
assertEquals(expectedMode, mode);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
new file mode 100644
index 000000000000..c5e6824099ed
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.flags.Flags;
+import android.location.provider.ProviderRequest;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+import com.android.server.timedetector.TimeDetectorInternal;
+
+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;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+@Presubmit
+@androidx.test.filters.SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssLocationProviderTest {
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.WARN)
+ .mockStatic(Settings.Global.class)
+ .build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private @Mock Context mContext;
+ private @Mock LocationManagerInternal mLocationManagerInternal;
+ private @Mock LocationManager mLocationManager;
+ private @Mock TimeDetectorInternal mTimeDetectorInternal;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private @Mock GnssMetrics mGnssMetrics;
+ private @Mock PowerManager mPowerManager;
+ private @Mock TelephonyManager mTelephonyManager;
+ private @Mock AppOpsManager mAppOpsManager;
+ private @Mock AlarmManager mAlarmManager;
+ private @Mock PowerManager.WakeLock mWakeLock;
+ private @Mock ContentResolver mContentResolver;
+ private @Mock UserManager mUserManager;
+ private @Mock UserHandle mUserHandle;
+ private Set<UserHandle> mUserHandleSet = new HashSet<>();
+
+ private GnssNative mGnssNative;
+
+ private GnssLocationProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn("mypackage").when(mContext).getPackageName();
+ doReturn("attribution").when(mContext).getAttributionTag();
+ doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+ doReturn(mPowerManager).when(mContext).getSystemService("power");
+ doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+ doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
+ doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
+ doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+ mUserHandleSet.add(mUserHandle);
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
+ doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
+ doReturn(mContentResolver).when(mContext).getContentResolver();
+ doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+ LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+ LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
+ FakeGnssHal fakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(fakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(
+ GnssNative.create(injector, mMockConfiguration)));
+ doReturn(true).when(mGnssNative).init();
+ GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
+ true).build();
+ doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
+
+ mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
+ mGnssNative.register();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(LocationManagerInternal.class);
+ LocalServices.removeServiceForTest(TimeDetectorInternal.class);
+ }
+
+ @Test
+ public void testStartNavigating() {
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_sameRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set the same request
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative, never()).stop();
+ verify(mGnssNative, times(1)).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_differentRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set a different request
+ providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).stop();
+ verify(mGnssNative, times(2)).start();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index 931b38dc2951..f8fe97e9af13 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -22,12 +22,14 @@ import android.content.pm.PackageManager.PERMISSION_DENIED
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.pm.UserInfo
import android.os.Build
+import android.os.UserHandle
import android.os.UserHandle.USER_SYSTEM
import android.util.Log
import com.android.server.testutils.any
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -177,4 +179,13 @@ class DeletePackageHelperTest {
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_INTERNAL_ERROR)
}
+
+ @Test
+ fun deletePackageLIFWithNonExistantPackage_isFalse() {
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
+ val result = dph.deletePackageLIF("a.nonexistent.package", UserHandle.of(USER_SYSTEM), true,
+ intArrayOf(0), 0, PackageRemovedInfo(), true)
+ assertFalse(result)
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
index 2003d04e1dbc..ca7de7c3f325 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -90,6 +90,10 @@ public class AggregatedPowerStatsTest {
private AggregatedPowerStats prepareAggregatePowerStats() {
AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
+
+ PowerStats ps = new PowerStats(mPowerComponentDescriptor);
+ stats.addPowerStats(ps, 0);
+
stats.addClockUpdate(1000, 456);
stats.setDuration(789);
@@ -100,7 +104,6 @@ public class AggregatedPowerStatsTest {
stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000);
- PowerStats ps = new PowerStats(mPowerComponentDescriptor);
ps.stats[0] = 100;
ps.stats[1] = 987;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 663af5da48d2..9c2834d31609 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -215,7 +215,7 @@ public class BatteryExternalStatsWorkerTest {
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
- super(Clock.SYSTEM_CLOCK, null);
+ super(Clock.SYSTEM_CLOCK, null, null, null);
mPowerProfile = new PowerProfile(context, true /* forTest */);
SparseArray<int[]> cpusByPolicy = new SparseArray<>();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index 55ffa1a15a6b..f9f32b2e7091 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -37,6 +37,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -97,6 +99,7 @@ public class BatteryStatsCpuTimesTest {
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
private MockClock mClocks;
+ private PowerStatsUidResolver mPowerStatsUidResolver;
private MockBatteryStatsImpl mBatteryStatsImpl;
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -105,7 +108,9 @@ public class BatteryStatsCpuTimesTest {
MockitoAnnotations.initMocks(this);
mClocks = new MockClock();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
+ Handler handler = new Handler(Looper.getMainLooper());
+ mPowerStatsUidResolver = new PowerStatsUidResolver();
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks, null, handler, mPowerStatsUidResolver)
.setTestCpuScalingPolicies()
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
@@ -374,7 +379,7 @@ public class BatteryStatsCpuTimesTest {
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
- mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, ownerUid);
final long[][] deltasUs = {
{9379, 3332409833484L}, {493247, 723234}, {3247819, 123348}
};
@@ -965,7 +970,7 @@ public class BatteryStatsCpuTimesTest {
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
- mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, ownerUid);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{32483274, 232349349, 123, 2398, 0},
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 5ebc6ca3f558..8d51592667c8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -39,14 +39,22 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
+import android.content.Context;
+import android.os.BatteryConsumer;
+import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BluetoothBatteryStats;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Parcel;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.util.SparseArray;
import android.view.Display;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,6 +73,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.time.Instant;
import java.util.List;
@LargeTest
@@ -93,6 +103,11 @@ public class BatteryStatsImplTest {
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
+ private Handler mHandler;
+ private PowerStatsStore mPowerStatsStore;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ @Mock
+ private PowerStatsExporter mPowerStatsExporter;
@Before
public void setUp() {
@@ -103,12 +118,23 @@ public class BatteryStatsImplTest {
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
when(mKernelWakelockReader.readKernelWakelockStats(
any(KernelWakelockStats.class))).thenReturn(mKernelWakelockStats);
- mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ mHandler = new Handler(bgThread.getLooper());
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, null, mHandler)
.setPowerProfile(mPowerProfile)
.setCpuScalingPolicies(mCpuScalingPolicies)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
.setKernelWakelockReader(mKernelWakelockReader);
+
+ final Context context = InstrumentationRegistry.getContext();
+ File systemDir = context.getCacheDir();
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler,
+ new AggregatedPowerStatsConfig());
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerStatsExporter,
+ mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore,
+ mMockClock);
}
@Test
@@ -754,4 +780,76 @@ public class BatteryStatsImplTest {
parcel.recycle();
return info;
}
+
+ @Test
+ public void storeBatteryUsageStatsOnReset() {
+ mBatteryStatsImpl.forceRecordAllHistory();
+
+ mMockClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
+ mMockClock.realtime = 7654321;
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.setOnBatteryLocked(mMockClock.realtime, mMockClock.uptime, true,
+ BatteryManager.BATTERY_STATUS_DISCHARGING, 50, 0);
+ // Will not save to PowerStatsStore because "saveBatteryUsageStatsOnReset" has not
+ // been called yet.
+ mBatteryStatsImpl.resetAllStatsAndHistoryLocked(
+ BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
+
+ assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+ mBatteryStatsImpl.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider,
+ mPowerStatsStore);
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.noteFlashlightOnLocked(42, mMockClock.realtime, mMockClock.uptime);
+ }
+
+ mMockClock.realtime += 60000;
+ mMockClock.currentTime += 60000;
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.noteFlashlightOffLocked(42, mMockClock.realtime, mMockClock.uptime);
+ }
+
+ mMockClock.realtime += 60000;
+ mMockClock.currentTime += 60000;
+
+ // Battery stats reset should have the side-effect of saving accumulated battery usage stats
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.resetAllStatsAndHistoryLocked(
+ BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
+
+ // Await completion
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+
+ List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+ assertThat(contents).hasSize(1);
+
+ PowerStatsSpan.Metadata metadata = contents.get(0);
+
+ PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+ BatteryUsageStatsSection.TYPE);
+ assertThat(span).isNotNull();
+
+ List<PowerStatsSpan.TimeFrame> timeFrames = span.getMetadata().getTimeFrames();
+ assertThat(timeFrames).hasSize(1);
+ assertThat(timeFrames.get(0).startMonotonicTime).isEqualTo(7654321);
+ assertThat(timeFrames.get(0).duration).isEqualTo(120000);
+
+ List<PowerStatsSpan.Section> sections = span.getSections();
+ assertThat(sections).hasSize(1);
+
+ PowerStatsSpan.Section section = sections.get(0);
+ assertThat(section.getType()).isEqualTo(BatteryUsageStatsSection.TYPE);
+ BatteryUsageStats bus = ((BatteryUsageStatsSection) section).getBatteryUsageStats();
+ assertThat(bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo(60000);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 7ef1a3fd0d83..24c67f83788b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -35,6 +35,8 @@ import android.app.usage.NetworkStatsManager;
import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -155,7 +157,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteStartWakeLocked_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -165,7 +169,7 @@ public class BatteryStatsNoteTest extends TestCase {
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -195,7 +199,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -205,7 +211,7 @@ public class BatteryStatsNoteTest extends TestCase {
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -216,7 +222,7 @@ public class BatteryStatsNoteTest extends TestCase {
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
clocks.realtime = clocks.uptime = 150;
- bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+ uidResolver.releaseIsolatedUid(ISOLATED_UID);
clocks.realtime = clocks.uptime = 220;
bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
@@ -237,8 +243,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -251,7 +258,7 @@ public class BatteryStatsNoteTest extends TestCase {
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -290,8 +297,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -304,7 +312,7 @@ public class BatteryStatsNoteTest extends TestCase {
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -314,7 +322,7 @@ public class BatteryStatsNoteTest extends TestCase {
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
clocks.realtime = clocks.uptime = 150;
- bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+ uidResolver.releaseIsolatedUid(ISOLATED_UID);
clocks.realtime = clocks.uptime = 220;
bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index b1da1fc88378..2e0ba0083850 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -28,9 +28,7 @@ import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.os.ConditionVariable;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -40,7 +38,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import org.junit.Rule;
@@ -72,10 +69,11 @@ public class BatteryUsageStatsProviderTest {
BatteryStatsImpl batteryStats = prepareBatteryStats();
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT);
+ provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
final List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
@@ -84,6 +82,15 @@ public class BatteryUsageStatsProviderTest {
.isEqualTo(20 * MINUTE_IN_MS);
assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
.isEqualTo(40 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(20 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(20 * MINUTE_IN_MS);
+ assertThat(uidBatteryConsumer
+ .getTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(20 * MINUTE_IN_MS);
assertThat(uidBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
.isWithin(PRECISION).of(2.0);
assertThat(
@@ -99,10 +106,11 @@ public class BatteryUsageStatsProviderTest {
BatteryStatsImpl batteryStats = prepareBatteryStats();
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder()
.includePowerComponents(
new int[]{BatteryConsumer.POWER_COMPONENT_AUDIO})
@@ -204,10 +212,11 @@ public class BatteryUsageStatsProviderTest {
}
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
Parcel in = Parcel.obtain();
@@ -292,11 +301,11 @@ public class BatteryUsageStatsProviderTest {
}
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider
- provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
Parcel parcel = Parcel.obtain();
@@ -352,27 +361,22 @@ public class BatteryUsageStatsProviderTest {
@Test
public void shouldUpdateStats() {
- Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- mStatsRule.getBatteryStats());
-
final List<BatteryUsageStatsQuery> queries = List.of(
new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(1000).build(),
new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(2000).build()
);
- mStatsRule.setTime(10500, 0);
- assertThat(provider.shouldUpdateStats(queries, 10000)).isFalse();
+ assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ 10500, 10000)).isFalse();
- mStatsRule.setTime(11500, 0);
- assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
+ assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ 11500, 10000)).isTrue();
}
@Test
public void testAggregateBatteryStats() {
Context context = InstrumentationRegistry.getContext();
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- MonotonicClock monotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
setTime(5 * MINUTE_IN_MS);
synchronized (batteryStats) {
@@ -381,14 +385,17 @@ public class BatteryUsageStatsProviderTest {
PowerStatsStore powerStatsStore = new PowerStatsStore(
new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
- new TestHandler(), null);
+ mStatsRule.getHandler(), null);
+ powerStatsStore.reset();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, powerStatsStore);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
+ mMockClock);
- batteryStats.setBatteryResetListener(reason ->
- powerStatsStore.storeBatteryUsageStats(monotonicClock.monotonicTime(),
- provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT)));
+ batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+ synchronized (batteryStats) {
+ batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
@@ -441,11 +448,16 @@ public class BatteryUsageStatsProviderTest {
}
setTime(95 * MINUTE_IN_MS);
+ // Await completion
+ ConditionVariable done = new ConditionVariable();
+ mStatsRule.getHandler().post(done::open);
+ done.block();
+
// Include the first and the second snapshot, but not the third or current
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS)
.build();
- final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
@@ -499,30 +511,19 @@ public class BatteryUsageStatsProviderTest {
when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE))
.thenReturn(span1);
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, powerStatsStore);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
+ mMockClock);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
.build();
- final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
assertThat(stats.getCustomPowerComponentNames())
.isEqualTo(batteryStats.getCustomEnergyConsumerNames());
assertThat(stats.getStatsDuration()).isEqualTo(1234);
}
- private static class TestHandler extends Handler {
- TestHandler() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- msg.getCallback().run();
- return true;
- }
- }
-
private static final Random sRandom = new Random();
/**
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 0b1095483dd0..e61dd0b41423 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -28,6 +28,8 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
import android.util.SparseArray;
@@ -57,6 +59,7 @@ public class BatteryUsageStatsRule implements TestRule {
private final PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
private final MockBatteryStatsImpl mBatteryStats;
+ private final Handler mHandler;
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
@@ -73,10 +76,13 @@ public class BatteryUsageStatsRule implements TestRule {
}
public BatteryUsageStatsRule(long currentTime, File historyDir) {
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ mHandler = new Handler(bgThread.getLooper());
mContext = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(mContext, true /* forTest */));
mMockClock.currentTime = currentTime;
- mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir, mHandler);
mBatteryStats.setPowerProfile(mPowerProfile);
mCpusByPolicy.put(0, new int[]{0, 1, 2, 3});
@@ -92,6 +98,10 @@ public class BatteryUsageStatsRule implements TestRule {
return mMockClock;
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
mPowerProfile.forceInitForTesting(mContext, xmlId);
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 07c486c6ce58..079ea2c7832f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -193,14 +193,14 @@ public class BatteryUsageStatsTest {
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
- 5321, 7432, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
+ 5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 745,
POWER_MODEL_UNDEFINED,
956, 1167, 1478,
true, 3554, 3776, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
444, 1110);
} else if (uidBatteryConsumer.getUid() == APP_UID2) {
assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
- 1111, 2222, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
+ 1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
BatteryConsumer.POWER_MODEL_POWER_PROFILE,
555, 666, 777,
true, 1777, 1888, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
@@ -269,7 +269,7 @@ public class BatteryUsageStatsTest {
.setStatsEndTimestamp(3000);
addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
- 1000, 2000,
+ 1000, 1500, 500,
300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800,
1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -312,13 +312,13 @@ public class BatteryUsageStatsTest {
.setStatsEndTimestamp(5000);
addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
- 4321, 5432,
+ 4321, 5400, 32,
123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION,
456, 567, 678,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
- 1111, 2222,
+ 1111, 2220, 2,
333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777,
1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
@@ -338,7 +338,8 @@ public class BatteryUsageStatsTest {
private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
- int timeInStateForeground, int timeInStateBackground, double screenPower,
+ int timeInProcessStateForeground, int timeInProcessStateBackground,
+ int timeInProcessStateForegroundService, double screenPower,
int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower,
int cpuDuration, int customComponentDuration, double cpuPowerForeground,
int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground,
@@ -348,8 +349,10 @@ public class BatteryUsageStatsTest {
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
uidBuilder
.setPackageWithHighestDrain(packageWithHighestDrain)
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
+ .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND, timeInProcessStateForeground)
+ .setTimeInProcessStateMs(PROCESS_STATE_BACKGROUND, timeInProcessStateBackground)
+ .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE,
+ timeInProcessStateForegroundService)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
.setConsumedPower(
@@ -446,7 +449,7 @@ public class BatteryUsageStatsTest {
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
if (uidBatteryConsumer.getUid() == APP_UID1) {
assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
- 1000, 2000, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
+ 1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
BatteryConsumer.POWER_MODEL_POWER_PROFILE,
500, 600, 800,
true, 1777, 1888, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
@@ -496,8 +499,9 @@ public class BatteryUsageStatsTest {
}
private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
- double consumedPower, String packageWithHighestDrain, int timeInStateForeground,
- int timeInStateBackground, int screenPower, int screenPowerModel, double cpuPower,
+ double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground,
+ int timeInProcessStateBackground, int timeInProcessStateForegroundService,
+ int screenPower, int screenPowerModel, double cpuPower,
int cpuPowerModel, double customComponentPower, int cpuDuration,
int customComponentDuration, boolean processStateDataIncluded,
double totalPowerForeground, double totalPowerBackground, double totalPowerFgs,
@@ -509,9 +513,16 @@ public class BatteryUsageStatsTest {
assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
packageWithHighestDrain);
assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+ UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+ UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(
+ timeInProcessStateBackground + timeInProcessStateForegroundService);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_FOREGROUND)).isEqualTo(timeInProcessStateForeground);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_BACKGROUND)).isEqualTo(timeInProcessStateBackground);
+ assertThat(uidBatteryConsumer.getTimeInProcessStateMs(
+ PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService);
assertThat(uidBatteryConsumer.getConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
assertThat(uidBatteryConsumer.getPowerModel(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
index 79084cc1b04d..8ca4ff6f86f5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
@@ -192,7 +192,7 @@ public class CpuAggregatedPowerStatsProcessorTest {
private static class MockPowerComponentAggregatedPowerStats extends
PowerComponentAggregatedPowerStats {
- private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private HashMap<String, long[]> mDeviceStats = new HashMap<>();
private HashMap<String, long[]> mUidStats = new HashMap<>();
@@ -203,10 +203,10 @@ public class CpuAggregatedPowerStatsProcessorTest {
MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
boolean useEnergyConsumers) {
super(config);
- mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
- mStatsLayout.addDeviceSectionUptime();
+ mStatsLayout.addDeviceSectionUsageDuration();
if (useEnergyConsumers) {
mStatsLayout.addDeviceSectionEnergyConsumers(2);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index bc211df5f143..64d5414bf66c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -19,6 +19,7 @@ package com.android.server.power.stats;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -56,6 +57,9 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CpuPowerStatsCollectorTest {
+ private static final int ISOLATED_UID = 99123;
+ private static final int UID_1 = 42;
+ private static final int UID_2 = 99;
private Context mContext;
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
@@ -63,6 +67,8 @@ public class CpuPowerStatsCollectorTest {
private PowerStats mCollectedStats;
private PowerProfile mPowerProfile;
@Mock
+ private PowerStatsUidResolver mUidResolver;
+ @Mock
private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
@Mock
private PowerStatsInternal mPowerStatsInternal;
@@ -76,6 +82,14 @@ public class CpuPowerStatsCollectorTest {
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
+ when(mUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
+ int uid = invocation.getArgument(0);
+ if (uid == ISOLATED_UID) {
+ return UID_2;
+ } else {
+ return uid;
+ }
+ });
}
@Test
@@ -156,14 +170,14 @@ public class CpuPowerStatsCollectorTest {
assertThat(descriptor.name).isEqualTo("cpu");
assertThat(descriptor.statsArrayLength).isEqualTo(13);
assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
layout.setTimeByScalingStep(deviceStats, 2, 42);
layout.setConsumedEnergy(deviceStats, 1, 43);
- layout.setUptime(deviceStats, 44);
+ layout.setUsageDuration(deviceStats, 44);
layout.setDevicePowerEstimate(deviceStats, 45);
long[] uidStats = new long[descriptor.uidStatsArrayLength];
@@ -173,10 +187,10 @@ public class CpuPowerStatsCollectorTest {
assertThat(layout.getCpuScalingStepCount()).isEqualTo(7);
assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42);
- assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2);
+ assertThat(layout.getEnergyConsumerCount()).isEqualTo(2);
assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43);
- assertThat(layout.getUptime(deviceStats)).isEqualTo(44);
+ assertThat(layout.getUsageDuration(deviceStats)).isEqualTo(44);
assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45);
@@ -195,14 +209,15 @@ public class CpuPowerStatsCollectorTest {
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
mockKernelCpuStats(new long[]{1111, 2222, 3333},
new SparseArray<>() {{
- put(42, new long[]{100, 200});
- put(99, new long[]{300, 600});
+ put(UID_1, new long[]{100, 200});
+ put(UID_2, new long[]{100, 150});
+ put(ISOLATED_UID, new long[]{200, 450});
}}, 0, 1234);
mMockClock.uptime = 1000;
@@ -219,19 +234,19 @@ public class CpuPowerStatsCollectorTest {
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(0);
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(0);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 0))
.isEqualTo(100);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 1))
.isEqualTo(200);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 0))
.isEqualTo(300);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 1))
.isEqualTo(600);
mockKernelCpuStats(new long[]{5555, 4444, 3333},
new SparseArray<>() {{
- put(42, new long[]{123, 234});
- put(99, new long[]{345, 678});
+ put(UID_1, new long[]{123, 234});
+ put(ISOLATED_UID, new long[]{245, 528});
}}, 1234, 3421);
mMockClock.uptime = 2000;
@@ -249,13 +264,13 @@ public class CpuPowerStatsCollectorTest {
// 700 * 1000 / 3500
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(200);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 0))
.isEqualTo(23);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 1))
.isEqualTo(34);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 0))
.isEqualTo(45);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 1))
.isEqualTo(78);
}
@@ -282,9 +297,9 @@ public class CpuPowerStatsCollectorTest {
private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
int defaultCpuPowerBracketsPerEnergyConsumer) {
CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
- mPowerProfile, mHandler, mMockKernelCpuStatsReader, () -> mPowerStatsInternal,
- () -> 3500, 60_000, mMockClock, defaultCpuPowerBrackets,
- defaultCpuPowerBracketsPerEnergyConsumer);
+ mPowerProfile, mHandler, mMockKernelCpuStatsReader, mUidResolver,
+ () -> mPowerStatsInternal, () -> 3500, 60_000, mMockClock,
+ defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer);
collector.addConsumer(stats -> mCollectedStats = stats);
collector.setEnabled(true);
return collector;
@@ -375,8 +390,8 @@ public class CpuPowerStatsCollectorTest {
}
private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
return layout.getScalingStepToPowerBracketMap();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 4150972ab69a..fb71ac83a8d9 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -61,16 +61,23 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
}
MockBatteryStatsImpl(Clock clock, File historyDirectory) {
- super(clock, historyDirectory);
+ this(clock, historyDirectory, new Handler(Looper.getMainLooper()));
+ }
+
+ MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler) {
+ this(clock, historyDirectory, handler, new PowerStatsUidResolver());
+ }
+
+ MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler,
+ PowerStatsUidResolver powerStatsUidResolver) {
+ super(clock, historyDirectory, handler, powerStatsUidResolver);
initTimersAndCounters();
setMaxHistoryBuffer(128 * 1024);
setExternalStatsSyncLocked(mExternalStatsSync);
informThatAllExternalStatsAreFlushed();
- // A no-op handler.
- mHandler = new Handler(Looper.getMainLooper()) {
- };
+ mHandler = handler;
mCpuUidFreqTimeReader = mock(KernelCpuUidFreqTimeReader.class);
mKernelWakelockReader = null;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index b52fc8a7c727..67049871f396 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -76,9 +76,18 @@ public class PowerStatsAggregatorTest {
@Test
public void stateUpdates() {
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+
mClock.currentTime = 1222156800000L; // An important date in world history
mHistory.forceRecordAllHistory();
+ powerStats.stats = new long[]{0};
+ powerStats.uidStats.put(TEST_UID, new long[]{0});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
@@ -87,10 +96,6 @@ public class PowerStatsAggregatorTest {
advance(1000);
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
- PowerStats powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{10000};
powerStats.uidStats.put(TEST_UID, new long[]{1234});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
@@ -181,17 +186,21 @@ public class PowerStatsAggregatorTest {
@Test
public void incompatiblePowerStats() {
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+
mHistory.forceRecordAllHistory();
+ powerStats.stats = new long[]{0};
+ powerStats.uidStats.put(TEST_UID, new long[]{0});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
BatteryConsumer.PROCESS_STATE_FOREGROUND);
advance(1000);
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
- PowerStats powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{10000};
powerStats.uidStats.put(TEST_UID, new long[]{1234});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
new file mode 100644
index 000000000000..3c482625b038
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsExporterTest {
+
+ private static final int APP_UID1 = 42;
+ private static final int APP_UID2 = 84;
+ private static final double TOLERANCE = 0.01;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+ .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
+ .setAveragePowerForCpuScalingPolicy(0, 360)
+ .setAveragePowerForCpuScalingStep(0, 0, 300)
+ .setCpuPowerBracketCount(1)
+ .setCpuPowerBracket(0, 0, 0);
+
+ private MockClock mClock = new MockClock();
+ private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+ private PowerStatsStore mPowerStatsStore;
+ private PowerStatsAggregator mPowerStatsAggregator;
+ private BatteryStatsHistory mHistory;
+ private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout;
+ private PowerStats.Descriptor mPowerStatsDescriptor;
+
+ @Before
+ public void setup() {
+ File storeDirectory = new File(getContext().getCacheDir(), getClass().getSimpleName());
+ clearDirectory(storeDirectory);
+
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies()));
+
+ mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
+ mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
+ mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+ mMonotonicClock, null);
+ mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
+
+ mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
+ mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
+ mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
+ mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
+ mCpuStatsArrayLayout.addUidSectionPowerEstimate();
+ PersistableBundle extras = new PersistableBundle();
+ mCpuStatsArrayLayout.toExtras(extras);
+
+ mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+ mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
+ mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+ }
+
+ @Test
+ public void breakdownByProcState_fullRange() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ true, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 1000, 10000);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03);
+
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03);
+
+ actual.close();
+ }
+
+ @Test
+ public void breakdownByProcState_subRange() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ true, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 3700, 6700);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 4.06);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70);
+
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 11.33);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33);
+
+ actual.close();
+ }
+
+ @Test
+ public void combinedProcessStates() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ false, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 1000, 10000);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
+ .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
+ // There shouldn't be any per-procstate data
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> uidScope.getConsumedPower(new BatteryConsumer.Dimensions(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND)));
+
+
+ actual.close();
+ }
+
+ private void recordBatteryHistory() {
+ PowerStats powerStats = new PowerStats(mPowerStatsDescriptor);
+ long[] uidStats1 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+ powerStats.uidStats.put(APP_UID1, uidStats1);
+ long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+ powerStats.uidStats.put(APP_UID2, uidStats2);
+
+ mHistory.forceRecordAllHistory();
+
+ mHistory.startRecordingHistory(1000, 1000, false);
+ mHistory.recordPowerStats(1000, 1000, powerStats);
+ mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false);
+ mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+ mHistory.recordProcessStateChange(1000, 1000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ mHistory.recordProcessStateChange(1000, 1000, APP_UID2,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 11111);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 10000);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 1111);
+ mHistory.recordPowerStats(1000, 1000, powerStats);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 12345);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 9876);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469);
+ mHistory.recordPowerStats(3000, 3000, powerStats);
+
+ mPowerStatsAggregator.aggregatePowerStats(0, 3500, stats -> {
+ mPowerStatsStore.storeAggregatedPowerStats(stats);
+ });
+
+ mHistory.recordProcessStateChange(4000, 4000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+ mHistory.recordStateStopEvent(4000, 4000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 54321);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 14321);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000);
+ mHistory.recordPowerStats(6000, 6000, powerStats);
+
+ mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> {
+ mPowerStatsStore.storeAggregatedPowerStats(stats);
+ });
+
+ mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+ mHistory.recordProcessStateChange(7000, 7000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ mHistory.recordProcessStateChange(7000, 7000, APP_UID2,
+ BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 23456);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 23456);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 0);
+ mHistory.recordPowerStats(8000, 8000, powerStats);
+
+ assertThat(mPowerStatsStore.getTableOfContents()).hasSize(2);
+ }
+
+ private void exportAggregatedPowerStats(BatteryUsageStats.Builder builder,
+ int monotonicStartTime, int monotonicEndTime) {
+ recordBatteryHistory();
+ PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
+ mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
+ exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime);
+ }
+
+ private void assertDevicePowerEstimate(String message, BatteryUsageStats bus, int componentId,
+ double expected) {
+ AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertWithMessage(message).that(consumer.getConsumedPower(componentId))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void assertAllAppsPowerEstimate(String message, BatteryUsageStats bus, int componentId,
+ double expected) {
+ AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ assertWithMessage(message).that(consumer.getConsumedPower(componentId))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
+ int componentId, int processState, double expected) {
+ List<UidBatteryConsumer> uidScopes = bus.getUidBatteryConsumers();
+ final UidBatteryConsumer uidScope = uidScopes.stream()
+ .filter(us -> us.getUid() == uid).findFirst().orElse(null);
+ assertWithMessage(message).that(uidScope).isNotNull();
+ assertWithMessage(message).that(uidScope.getConsumedPower(
+ new BatteryConsumer.Dimensions(componentId, processState)))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index 0e58787a6a9b..7257a94cbb9a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -27,9 +27,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.os.BatteryConsumer;
-import android.os.BatteryManager;
-import android.os.BatteryUsageStats;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -59,13 +56,13 @@ public class PowerStatsSchedulerTest {
private MockClock mClock = new MockClock();
private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
private MockBatteryStatsImpl mBatteryStats;
- private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private PowerStatsScheduler mPowerStatsScheduler;
private PowerProfile mPowerProfile;
private PowerStatsAggregator mPowerStatsAggregator;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
@Before
+ @SuppressWarnings("GuardedBy")
public void setup() {
final Context context = InstrumentationRegistry.getContext();
@@ -83,11 +80,10 @@ public class PowerStatsSchedulerTest {
mPowerProfile = mock(PowerProfile.class);
when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0);
mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile);
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
mPowerStatsAggregator = mock(PowerStatsAggregator.class);
mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock,
- mMonotonicClock, mHandler, mBatteryStats, mBatteryUsageStatsProvider);
+ mMonotonicClock, mHandler, mBatteryStats);
}
@Test
@@ -176,70 +172,6 @@ public class PowerStatsSchedulerTest {
}
@Test
- public void storeBatteryUsageStatsOnReset() {
- mBatteryStats.forceRecordAllHistory();
- synchronized (mBatteryStats) {
- mBatteryStats.setOnBatteryLocked(mClock.realtime, mClock.uptime, true,
- BatteryManager.BATTERY_STATUS_DISCHARGING, 50, 0);
- }
-
- mPowerStatsScheduler.start(/* schedulePeriodicPowerStatsCollection */false);
-
- assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
-
- mPowerStatsScheduler.start(true);
-
- synchronized (mBatteryStats) {
- mBatteryStats.noteFlashlightOnLocked(42, mClock.realtime, mClock.uptime);
- }
-
- mClock.realtime += 60000;
- mClock.currentTime += 60000;
-
- synchronized (mBatteryStats) {
- mBatteryStats.noteFlashlightOffLocked(42, mClock.realtime, mClock.uptime);
- }
-
- mClock.realtime += 60000;
- mClock.currentTime += 60000;
-
- // Battery stats reset should have the side-effect of saving accumulated battery usage stats
- synchronized (mBatteryStats) {
- mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
- }
-
- // Await completion
- ConditionVariable done = new ConditionVariable();
- mHandler.post(done::open);
- done.block();
-
- List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
- assertThat(contents).hasSize(1);
-
- PowerStatsSpan.Metadata metadata = contents.get(0);
-
- PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
- BatteryUsageStatsSection.TYPE);
- assertThat(span).isNotNull();
-
- List<PowerStatsSpan.TimeFrame> timeFrames = span.getMetadata().getTimeFrames();
- assertThat(timeFrames).hasSize(1);
- assertThat(timeFrames.get(0).startMonotonicTime).isEqualTo(7654321);
- assertThat(timeFrames.get(0).duration).isEqualTo(120000);
-
- List<PowerStatsSpan.Section> sections = span.getSections();
- assertThat(sections).hasSize(1);
-
- PowerStatsSpan.Section section = sections.get(0);
- assertThat(section.getType()).isEqualTo(BatteryUsageStatsSection.TYPE);
- BatteryUsageStats bus = ((BatteryUsageStatsSection) section).getBatteryUsageStats();
- assertThat(bus.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isEqualTo(60000);
- }
-
- @Test
public void alignToWallClock() {
// Expect the aligned value to be adjusted by 1 min 30 sec - rounded to the next 15 min
assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java
new file mode 100644
index 000000000000..60b2541fa7a8
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsUidResolverTest {
+
+ private final PowerStatsUidResolver mResolver = new PowerStatsUidResolver();
+ @Mock
+ PowerStatsUidResolver.Listener mListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void addAndRemoveIsolatedUid() {
+ mResolver.addListener(mListener);
+ mResolver.noteIsolatedUidAdded(42, 314);
+ verify(mListener).onIsolatedUidAdded(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+
+ mResolver.noteIsolatedUidRemoved(42, 314);
+ verify(mListener).onBeforeIsolatedUidRemoved(42, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(42);
+
+ verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void retainAndRemoveIsolatedUid() {
+ mResolver.addListener(mListener);
+ mResolver.noteIsolatedUidAdded(42, 314);
+ verify(mListener).onIsolatedUidAdded(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+
+ mResolver.retainIsolatedUid(42);
+
+ mResolver.noteIsolatedUidRemoved(42, 314);
+ verify(mListener).onBeforeIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+ verifyNoMoreInteractions(mListener);
+
+ mResolver.releaseIsolatedUid(42);
+ verify(mListener).onAfterIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(42);
+
+ verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void removeUidsInRange() {
+ mResolver.noteIsolatedUidAdded(1, 314);
+ mResolver.noteIsolatedUidAdded(2, 314);
+ mResolver.noteIsolatedUidAdded(3, 314);
+ mResolver.noteIsolatedUidAdded(4, 314);
+ mResolver.noteIsolatedUidAdded(6, 314);
+ mResolver.noteIsolatedUidAdded(8, 314);
+ mResolver.noteIsolatedUidAdded(10, 314);
+
+ mResolver.addListener(mListener);
+
+ mResolver.releaseUidsInRange(4, 4); // Single
+ verify(mListener).onAfterIsolatedUidRemoved(4, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 2, 3, 6, 8, 10]
+
+ mResolver.releaseUidsInRange(2, 3); // Inclusive
+ verify(mListener).onAfterIsolatedUidRemoved(2, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(3, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 6, 8, 10]
+
+ mResolver.releaseUidsInRange(5, 9); // Exclusive
+ verify(mListener).onAfterIsolatedUidRemoved(6, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(8, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 10]
+
+ mResolver.releaseUidsInRange(5, 9); // Empty
+ verifyNoMoreInteractions(mListener);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index 1002fba3d60d..88ca02933450 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import android.app.ActivityManagerInternal;
+import android.app.pinner.PinnedFileStat;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,22 +48,19 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.Optional;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -138,6 +136,13 @@ public class PinnerServiceTest {
protected void publishBinderService(PinnerService service, Binder binderService) {
// Suppress this for testing, it's not needed and causes conflitcs.
}
+
+ @Override
+ protected PinnerService.PinnedFile pinFileInternal(String fileToPin,
+ int maxBytesToPin, boolean attemptPinIntrospection) {
+ return new PinnerService.PinnedFile(-1,
+ maxBytesToPin, fileToPin, maxBytesToPin);
+ }
};
}
@@ -202,20 +207,27 @@ public class PinnerServiceTest {
return cw.toString();
}
- private int getPinnedSize(PinnerService pinnerService) throws Exception {
- return getPinnedSizeImpl(pinnerService, "Total size: ");
+ private long getPinnedSize(PinnerService pinnerService) {
+ long totalBytesPinned = 0;
+ for (PinnedFileStat stat : pinnerService.getPinnerStats()) {
+ totalBytesPinned += stat.getBytesPinned();
+ }
+ return totalBytesPinned;
}
- private int getPinnedAnonSize(PinnerService pinnerService) throws Exception {
- return getPinnedSizeImpl(pinnerService, "Pinned anon region: ");
+ private int getPinnedAnonSize(PinnerService pinnerService) {
+ List<PinnedFileStat> anonStats = pinnerService.getPinnerStats().stream()
+ .filter(pf -> pf.getGroupName().equals(PinnerService.ANON_REGION_STAT_NAME))
+ .toList();
+ int totalAnon = 0;
+ for (PinnedFileStat anonStat : anonStats) {
+ totalAnon += anonStat.getBytesPinned();
+ }
+ return totalAnon;
}
- private int getPinnedSizeImpl(PinnerService pinnerService, String sizeToken) throws Exception {
- String dumpOutput = getPinnerServiceDump(pinnerService);
- BufferedReader bufReader = new BufferedReader(new StringReader(dumpOutput));
- Optional<Integer> size = bufReader.lines().filter(s -> s.contains(sizeToken))
- .map(s -> Integer.valueOf(s.substring(sizeToken.length()))).findAny();
- return size.orElse(-1);
+ private long getTotalPinnedFiles(PinnerService pinnerService) {
+ return pinnerService.getPinnerStats().stream().count();
}
private void setDeviceConfigPinnedAnonSize(long size) {
@@ -227,7 +239,6 @@ public class PinnerServiceTest {
}
@Test
- @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeApp() throws Exception {
// Enable HOME app pinning
mContext.getOrCreateTestableResources()
@@ -245,15 +256,13 @@ public class PinnerServiceTest {
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps.get(KEY_HOME)).isNotNull();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
- assertThat(totalPinnedSizeBytes).isGreaterThan(0);
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
+ assertThat(getTotalPinnedFiles(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@Test
- @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeAppOnBootCompleted() throws Exception {
// Enable HOME app pinning
mContext.getOrCreateTestableResources()
@@ -271,9 +280,7 @@ public class PinnerServiceTest {
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps.get(KEY_HOME)).isNotNull();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
- assertThat(totalPinnedSizeBytes).isGreaterThan(0);
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@@ -294,12 +301,24 @@ public class PinnerServiceTest {
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps).isEmpty();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
+ long totalPinnedSizeBytes = getPinnedSize(pinnerService);
assertThat(totalPinnedSizeBytes).isEqualTo(0);
int pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
- assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(0);
+
+ unpinAll(pinnerService);
+ }
+
+ @Test
+ public void testPinFile() throws Exception {
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
+
+ pinnerService.pinFile("test_file", 4096, null, "my_group");
+
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
+ assertThat(getTotalPinnedFiles(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@@ -341,11 +360,8 @@ public class PinnerServiceTest {
waitForPinnerService(pinnerService);
// An empty anon region should clear the associated status entry.
pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
- assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(0);
unpinAll(pinnerService);
}
-
- // TODO: Add test to check that the pages we expect to be pinned are actually pinned
-
}
diff --git a/services/tests/servicestests/src/com/android/server/SecurityStateTest.java b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
new file mode 100644
index 000000000000..fc91e47534f1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import static com.android.server.SecurityStateManagerService.KERNEL_RELEASE_PATTERN;
+import static com.android.server.SecurityStateManagerService.VENDOR_SECURITY_PATCH_PROPERTY_KEY;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.regex.Matcher;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SecurityStateTest {
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Context mMockContext;
+
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Mock
+ private Resources mMockResources;
+
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER_VERSION = "2023-12-01";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE = "com.google.android.gms";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE_VERSION = "2023-12-05";
+ private static final String[] SECURITY_STATE_PACKAGES =
+ new String[]{DEFAULT_SECURITY_STATE_PACKAGE};
+
+ @Before
+ public void setUp() throws Exception {
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockContext.getString(R.string.config_defaultModuleMetadataProvider))
+ .thenReturn(DEFAULT_MODULE_METADATA_PROVIDER);
+ when(mMockPackageManager.getPackageInfo(anyString(), anyInt()))
+ .thenReturn(new PackageInfo());
+ PackageInfo moduleMetadataPackageInfo = new PackageInfo();
+ moduleMetadataPackageInfo.versionName = DEFAULT_MODULE_METADATA_PROVIDER_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_MODULE_METADATA_PROVIDER, 0))
+ .thenReturn(moduleMetadataPackageInfo);
+ PackageInfo securityStatePackageInfo = new PackageInfo();
+ securityStatePackageInfo.versionName = DEFAULT_SECURITY_STATE_PACKAGE_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_SECURITY_STATE_PACKAGE, 0))
+ .thenReturn(securityStatePackageInfo);
+ when(mMockResources.getStringArray(R.array.config_securityStatePackages))
+ .thenReturn(SECURITY_STATE_PACKAGES);
+ }
+
+ @Test
+ public void testGetGlobalSecurityState_returnsBundle() {
+ SecurityStateManagerService securityState = new SecurityStateManagerService(mMockContext);
+
+ Bundle bundle = securityState.getGlobalSecurityState();
+
+ assertEquals(bundle.getString(KEY_SYSTEM_SPL), Build.VERSION.SECURITY_PATCH);
+ assertEquals(bundle.getString(KEY_VENDOR_SPL),
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ String kernelVersion = "";
+ if (matcher.matches()) {
+ kernelVersion = matcher.group(1);
+ }
+ assertEquals(bundle.getString(KEY_KERNEL_VERSION), kernelVersion);
+ assertEquals(bundle.getString(DEFAULT_MODULE_METADATA_PROVIDER),
+ DEFAULT_MODULE_METADATA_PROVIDER_VERSION);
+ assertEquals(bundle.getString(DEFAULT_SECURITY_STATE_PACKAGE),
+ DEFAULT_SECURITY_STATE_PACKAGE_VERSION);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogTest.java b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
new file mode 100644
index 000000000000..34ac47e1fe96
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/WatchdogTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2009 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Handler;
+import android.os.SimpleClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.Watchdog.HandlerChecker;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.ZoneOffset;
+
+/** Test class for {@link Watchdog}. */
+@RunWith(AndroidJUnit4.class)
+public class WatchdogTest {
+ private static final int TIMEOUT_MS = 10;
+
+ private TestClock mClock;
+ private Handler mHandler;
+ private HandlerChecker mChecker;
+
+ @Before
+ public void setUp() {
+ mClock = new TestClock();
+ mHandler = mock(Handler.class);
+ mChecker =
+ new HandlerChecker(mHandler, "monitor thread", new Object(), mClock) {
+ @Override
+ public boolean isHandlerPolling() {
+ return false;
+ }
+ };
+ }
+
+ @Test
+ public void checkerPausedUntilResume() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.pauseLocked("pausing");
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ mChecker.resumeLocked("resuming");
+ mChecker.scheduleCheckLocked(10);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkerPausedUntilDeadline() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.pauseForLocked(10, "pausing");
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ mClock.advanceBy(5);
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout. Watchdog should not be paused anymore.
+ mClock.advanceBy(6);
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkerPausedDuringScheduledRun() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ mClock.advanceBy(5);
+ mChecker.pauseForLocked(10, "pausing");
+ verifyNoMoreInteractions(mHandler);
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout. Watchdog should not be paused anymore.
+ mClock.advanceBy(11);
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(Watchdog.WAITING, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void blockedThread() {
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ assertEquals(mChecker.getCompletionStateLocked(), Watchdog.WAITING);
+
+ mClock.advanceBy(6);
+ assertEquals(Watchdog.WAITED_UNTIL_PRE_WATCHDOG, mChecker.getCompletionStateLocked());
+
+ // Above the 10s timeout.
+ mClock.advanceBy(6);
+ assertEquals(Watchdog.OVERDUE, mChecker.getCompletionStateLocked());
+ }
+
+ @Test
+ public void checkNothingBlocked() {
+ Watchdog.Monitor monitor = mock(Watchdog.Monitor.class);
+ mChecker.addMonitorLocked(monitor);
+
+ mChecker.scheduleCheckLocked(TIMEOUT_MS);
+ // scheduleCheckLocked calls #postAtFrontOfQueue which will call mChecker.run().
+ mChecker.run();
+ assertEquals(Watchdog.COMPLETED, mChecker.getCompletionStateLocked());
+ verify(monitor).monitor();
+ }
+
+ private static class TestClock extends SimpleClock {
+ long mNowMillis = 1;
+
+ TestClock() {
+ super(ZoneOffset.UTC);
+ }
+
+ @Override
+ public long millis() {
+ return mNowMillis;
+ }
+
+ public void advanceBy(long millis) {
+ mNowMillis += millis;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 82efdd3ce40a..800350a7d326 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -85,9 +85,9 @@ import com.android.internal.compat.IPlatformCompat;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -156,7 +156,7 @@ public class AccessibilityManagerServiceTest {
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
- @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationConnectionManager mMockMagnificationConnectionManager;
@Mock private MagnificationController mMockMagnificationController;
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
@@ -180,8 +180,8 @@ public class AccessibilityManagerServiceTest {
UserManagerInternal.class, mMockUserManagerInternal);
mInputFilter = Mockito.mock(FakeInputFilter.class);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationMgr);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
when(mMockMagnificationController.supportWindowMagnification()).thenReturn(true);
@@ -530,7 +530,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -547,7 +547,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -565,7 +565,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -583,7 +583,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -602,7 +602,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -616,7 +616,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -630,7 +630,8 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -644,7 +645,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@SmallTest
@@ -659,7 +660,8 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -674,7 +676,7 @@ public class AccessibilityManagerServiceTest {
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index a02807fe766c..7829fcc4b44d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -39,9 +39,9 @@ import android.accessibilityservice.MagnificationConfig;
import android.graphics.Region;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import org.junit.Before;
import org.junit.Test;
@@ -66,21 +66,21 @@ public class MagnificationProcessorTest {
@Mock
private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock
- private WindowMagnificationManager mMockWindowMagnificationManager;
+ private MagnificationConnectionManager mMockMagnificationConnectionManager;
FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
- WindowMagnificationManagerStub mWindowMagnificationManagerStub;
+ MagnificationManagerStub mMagnificationManagerStub;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
mMockFullScreenMagnificationController);
- mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
- mMockWindowMagnificationManager);
+ mMagnificationManagerStub = new MagnificationManagerStub(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationManager);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
}
@@ -194,7 +194,7 @@ public class MagnificationProcessorTest {
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ }).when(mMockMagnificationConnectionManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
any());
final Region result = new Region();
@@ -286,7 +286,7 @@ public class MagnificationProcessorTest {
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
- verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false,
+ verify(mMockMagnificationConnectionManager).disableWindowMagnification(TEST_DISPLAY, false,
null);
}
@@ -296,7 +296,7 @@ public class MagnificationProcessorTest {
mMagnificationProcessor.resetAllIfNeeded(connectionId);
verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId));
- verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId));
+ verify(mMockMagnificationConnectionManager).resetAllIfNeeded(eq(connectionId));
}
@Test
@@ -450,7 +450,7 @@ public class MagnificationProcessorTest {
.setActivated(false).build();
mMagnificationProcessor.setMagnificationConfig(TEST_DISPLAY, config, false, SERVICE_ID);
- verify(mMockWindowMagnificationManager)
+ verify(mMockMagnificationConnectionManager)
.disableWindowMagnification(eq(TEST_DISPLAY), anyBoolean());
}
@@ -481,11 +481,11 @@ public class MagnificationProcessorTest {
mFullScreenMagnificationControllerStub.resetAndStubMethods();
mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
config.getCenterX(), config.getCenterY(), false, SERVICE_ID);
- mWindowMagnificationManagerStub.deactivateIfNeed();
+ mMagnificationManagerStub.deactivateIfNeed();
} else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
- mWindowMagnificationManagerStub.resetAndStubMethods();
- mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
- config.getCenterX(), config.getCenterY());
+ mMagnificationManagerStub.resetAndStubMethods();
+ mMockMagnificationConnectionManager.enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY());
mFullScreenMagnificationControllerStub.deactivateIfNeed();
}
}
@@ -568,26 +568,26 @@ public class MagnificationProcessorTest {
}
}
- private static class WindowMagnificationManagerStub {
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private static class MagnificationManagerStub {
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
private float mScale = 1.0f;
private float mCenterX = 0;
private float mCenterY = 0;
private boolean mIsEnabled = false;
- WindowMagnificationManagerStub(
- WindowMagnificationManager windowMagnificationManager) {
- mWindowMagnificationManager = windowMagnificationManager;
+ MagnificationManagerStub(
+ MagnificationConnectionManager magnificationConnectionManager) {
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
private void stubMethods() {
- doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+ doAnswer(invocation -> mScale).when(mMagnificationConnectionManager).getScale(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+ doAnswer(invocation -> mCenterX).when(mMagnificationConnectionManager).getCenterX(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+ doAnswer(invocation -> mCenterY).when(mMagnificationConnectionManager).getCenterY(
TEST_DISPLAY);
- doAnswer(invocation -> mIsEnabled).when(mWindowMagnificationManager)
+ doAnswer(invocation -> mIsEnabled).when(mMagnificationConnectionManager)
.isWindowMagnifierEnabled(TEST_DISPLAY);
Answer enableWindowMagnificationStubAnswer = invocation -> {
@@ -598,10 +598,10 @@ public class MagnificationProcessorTest {
return true;
};
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat());
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat(), any(), anyInt());
Answer disableWindowMagnificationStubAnswer = invocation -> {
@@ -609,15 +609,15 @@ public class MagnificationProcessorTest {
return true;
};
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean());
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean(), any());
}
public void resetAndStubMethods() {
- Mockito.reset(mWindowMagnificationManager);
+ Mockito.reset(mMagnificationConnectionManager);
stubMethods();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index fd2cf6d4bb5f..3b39160643d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -540,18 +540,23 @@ public class FullScreenMagnificationGestureHandlerTest {
twoFingerTap();
assertIn(STATE_ACTIVATED);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
goFromStateIdleTo(STATE_ACTIVATED);
+ reset(mMockMagnificationLogger);
twoFingerTap();
twoFingerTap();
twoFingerTap();
assertIn(STATE_IDLE);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(false);
}
@Test
@@ -564,6 +569,8 @@ public class FullScreenMagnificationGestureHandlerTest {
twoFingerTapAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@@ -576,6 +583,8 @@ public class FullScreenMagnificationGestureHandlerTest {
twoFingerSwipeAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 24ad976f6e45..3843e2507df6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -33,8 +33,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static java.lang.Float.NaN;
@@ -76,7 +74,7 @@ import org.mockito.invocation.InvocationOnMock;
/**
* Tests for WindowMagnificationManager.
*/
-public class WindowMagnificationManagerTest {
+public class MagnificationConnectionManagerTest {
private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
private static final int SERVICE_ID = 1;
@@ -91,9 +89,9 @@ public class WindowMagnificationManagerTest {
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@Mock
- private WindowMagnificationManager.Callback mMockCallback;
+ private MagnificationConnectionManager.Callback mMockCallback;
private MockContentResolver mResolver;
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
@Before
public void setUp() throws RemoteException {
@@ -102,7 +100,7 @@ public class WindowMagnificationManagerTest {
LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
when(mContext.getContentResolver()).thenReturn(mResolver);
@@ -122,11 +120,11 @@ public class WindowMagnificationManagerTest {
final Context context = ApplicationProvider.getApplicationContext();
context.getMainThreadHandler().postDelayed(
() -> {
- mWindowMagnificationManager.setConnection(
+ mMagnificationConnectionManager.setConnection(
connect ? mMockConnection.getConnection() : null);
}, 10);
} else {
- mWindowMagnificationManager.setConnection(
+ mMagnificationConnectionManager.setConnection(
connect ? mMockConnection.getConnection() : null);
}
return true;
@@ -135,17 +133,17 @@ public class WindowMagnificationManagerTest {
@Test
public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertTrue(mMagnificationConnectionManager.isConnected());
verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
}
@Test
public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ assertTrue(mMagnificationConnectionManager.isConnected());
verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
verify(mMockConnection.getConnection()).setConnectionCallback(
any(IWindowMagnificationConnectionCallback.class));
@@ -153,31 +151,31 @@ public class WindowMagnificationManagerTest {
@Test
public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMockConnection.getDeathRecipient().binderDied();
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ assertFalse(mMagnificationConnectionManager.isConnected());
verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
0);
}
/**
- * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
- * and then the former connection is called by thread B. In this situation we should keep the
+ * This test simulates {@link MagnificationConnectionManager#setConnection} is called by thread
+ * A and then the former connection is called by thread B. In this situation we should keep the
* new connection.
*/
@Test
public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
MockWindowMagnificationConnection secondConnection =
new MockWindowMagnificationConnection();
- mWindowMagnificationManager.setConnection(secondConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(secondConnection.getConnection());
mMockConnection.getDeathRecipient().binderDied();
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ assertTrue(mMagnificationConnectionManager.isConnected());
verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
verify(secondConnection.asBinder(), never()).unlinkToDeath(
secondConnection.getDeathRecipient(), 0);
@@ -185,20 +183,20 @@ public class WindowMagnificationManagerTest {
@Test
public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.setConnection(null);
+ mMagnificationConnectionManager.setConnection(null);
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ assertFalse(mMagnificationConnectionManager.isConnected());
verify(mMockConnection.getConnection()).setConnectionCallback(null);
}
@Test
public void enableWithAnimation_hasConnection_enableWindowMagnification()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
eq(200f), eq(300f), eq(0f), eq(0f), notNull());
@@ -207,9 +205,9 @@ public class WindowMagnificationManagerTest {
@Test
public void enableWithCallback_hasConnection_enableWindowMagnification()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
mAnimationCallback, SERVICE_ID);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
@@ -221,10 +219,10 @@ public class WindowMagnificationManagerTest {
@Test
public void disable_hasConnectionAndEnabled_disableWindowMagnification()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
notNull());
@@ -233,10 +231,10 @@ public class WindowMagnificationManagerTest {
@Test
public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false,
mAnimationCallback);
verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
@@ -246,28 +244,28 @@ public class WindowMagnificationManagerTest {
@Test
public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
public void getPersistedScale() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f);
+ assertEquals(mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY), 2.5f);
}
@Test
public void persistScale_setValue_expectedValueInProvider() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
assertEquals(Settings.Secure.getFloatForUser(mResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
@@ -276,11 +274,12 @@ public class WindowMagnificationManagerTest {
@Test
public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- final float persistedScale = mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ final float persistedScale =
+ mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 1.0f);
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 1.0f);
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
assertEquals(Settings.Secure.getFloatForUser(mResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
@@ -289,50 +288,53 @@ public class WindowMagnificationManagerTest {
@Test
public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), 2.5f);
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY), 2.5f);
}
@Test
public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY),
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
MagnificationScaleProvider.MAX_SCALE);
}
@FlakyTest(bugId = 297879435)
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
- WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
- spyWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- spyWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, /* shown */ true);
+ MagnificationConnectionManager spyMagnificationConnectionManager = spy(
+ mMagnificationConnectionManager);
+ spyMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3.0f, 50f, 50f);
+ spyMagnificationConnectionManager.onImeWindowVisibilityChanged(
+ TEST_DISPLAY, /* shown */ true);
- spyWindowMagnificationManager.processScroll(TEST_DISPLAY, 10f, 10f);
+ spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
- verify(spyWindowMagnificationManager).logTrackingTypingFocus(anyLong());
+ verify(spyMagnificationConnectionManager).logTrackingTypingFocus(anyLong());
}
@Test
public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection(), never())
@@ -345,16 +347,16 @@ public class WindowMagnificationManagerTest {
throws RemoteException {
final float distanceX = 10f;
final float distanceY = 10f;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+ mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection(), never())
@@ -364,15 +366,15 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.inset(-10, -10);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection(), never())
@@ -381,14 +383,14 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection(), never())
@@ -398,15 +400,15 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
@@ -417,16 +419,16 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection(), never())
@@ -436,17 +438,17 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
final Rect requestedRect = outRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
@@ -456,58 +458,58 @@ public class WindowMagnificationManagerTest {
@Test
public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
final Rect requestedRect = beforeRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
+ mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
assertEquals(afterRegion, beforeRegion);
}
@Test
public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
final Rect requestedRect = beforeRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
assertEquals(afterRegion, beforeRegion);
}
@Test
public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
final Rect requestedRect = beforeRegion.getBounds();
requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
// Enabling a window magnifier again will turn on the tracking typing focus functionality.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
@@ -517,43 +519,43 @@ public class WindowMagnificationManagerTest {
@Test
public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
- mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
+ mMagnificationConnectionManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
}
@Test
public void showMagnificationButton_hasConnection_invokeConnectionMethod()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
+ mMagnificationConnectionManager.showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- mWindowMagnificationManager.removeMagnificationButton(TEST_DISPLAY);
+ mMagnificationConnectionManager.removeMagnificationButton(TEST_DISPLAY);
verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
}
@Test
public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
+ mMagnificationConnectionManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
}
@Test
public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
final float testScale = 3f;
- mWindowMagnificationManager.onUserMagnificationScaleChanged(
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
CURRENT_USER_ID, TEST_DISPLAY, testScale);
verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
@@ -561,8 +563,8 @@ public class WindowMagnificationManagerTest {
@Test
public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
new Rect(0, 0, 500, 500));
PointF[] pointersLocation = new PointF[2];
@@ -570,15 +572,15 @@ public class WindowMagnificationManagerTest {
pointersLocation[1] = new PointF(300, 400);
MotionEvent event = generatePointersDownEvent(pointersLocation);
- assertEquals(mWindowMagnificationManager.pointersInWindow(TEST_DISPLAY, event), 1);
+ assertEquals(mMagnificationConnectionManager.pointersInWindow(TEST_DISPLAY, event), 1);
}
@Test
public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
final float newScale = 4.0f;
final boolean updatePersistence = true;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
mMockConnection.getConnectionCallback().onPerformScaleAction(
TEST_DISPLAY, newScale, updatePersistence);
@@ -590,8 +592,8 @@ public class WindowMagnificationManagerTest {
@Test
public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
@@ -600,22 +602,22 @@ public class WindowMagnificationManagerTest {
@Test
public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
mMockConnection.getDeathRecipient().binderDied();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
public void
requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- assertTrue(mWindowMagnificationManager.requestConnection(false));
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
@@ -623,40 +625,40 @@ public class WindowMagnificationManagerTest {
@Test
public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
}
@Test
public void isConnected_requestConnection_expectedValue() throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- assertTrue(mWindowMagnificationManager.isConnected());
+ mMagnificationConnectionManager.requestConnection(true);
+ assertTrue(mMagnificationConnectionManager.isConnected());
- mWindowMagnificationManager.requestConnection(false);
- assertFalse(mWindowMagnificationManager.isConnected());
+ mMagnificationConnectionManager.requestConnection(false);
+ assertFalse(mMagnificationConnectionManager.isConnected());
}
@Test
public void requestConnection_registerAndUnregisterBroadcastReceiver() {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
- assertTrue(mWindowMagnificationManager.requestConnection(false));
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
}
@Test
public void requestConnectionToNull_expectedGetterResults() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
- mWindowMagnificationManager.requestConnection(false);
+ mMagnificationConnectionManager.requestConnection(false);
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
assertTrue(bounds.isEmpty());
}
@@ -664,9 +666,10 @@ public class WindowMagnificationManagerTest {
public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
throws RemoteException {
stubSetConnection(true);
- mWindowMagnificationManager.requestConnection(true);
+ mMagnificationConnectionManager.requestConnection(true);
- assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1));
+ assertTrue(mMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3f, 1, 1));
// Invoke enableWindowMagnification if the connection is connected.
verify(mMockConnection.getConnection()).enableWindowMagnification(
@@ -676,69 +679,73 @@ public class WindowMagnificationManagerTest {
@Test
public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
}
@Test
public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
final int serviceId2 = SERVICE_ID + 1;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, serviceId2);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, serviceId2);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
}
@Test
public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
- mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
+ mMagnificationConnectionManager.mScreenStateReceiver.onReceive(mContext,
new Intent(Intent.ACTION_SCREEN_OFF));
verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
public void centerGetter_enabledOnTestDisplay_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
}
@Test
public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
eq(100f), eq(200f), eq(0f), eq(0f), notNull());
@@ -747,12 +754,12 @@ public class WindowMagnificationManagerTest {
@Test
public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
@@ -760,48 +767,48 @@ public class WindowMagnificationManagerTest {
@Test
public void magnifierGetters_disabled_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
assertTrue(bounds.isEmpty());
}
@Test
public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
- mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
+ mMagnificationConnectionManager.onDisplayRemoved(TEST_DISPLAY);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
}
@Test
public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
Mockito.reset(mMockCallback);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
anyBoolean());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index cfd0289e5650..8f85f11b7c49 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -39,7 +39,7 @@ import org.mockito.MockitoAnnotations;
/**
* Tests for MagnificationConnectionWrapper. We don't test {@code
* MagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
- * {@link WindowMagnificationManagerTest}.
+ * {@link MagnificationConnectionManagerTest}.
*/
public class MagnificationConnectionWrapperTest {
@@ -73,9 +73,9 @@ public class MagnificationConnectionWrapperTest {
}
@Test
- public void setScale() throws RemoteException {
- mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
- verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mConnectionWrapper.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+ verify(mConnection).setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index d4c6fad99645..e8cdf35dee13 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -131,7 +131,7 @@ public class MagnificationControllerTest {
private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
private MockWindowMagnificationConnection mMockConnection;
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockContentResolver mMockResolver;
private MagnificationController mMagnificationController;
private final WindowMagnificationMgrCallbackDelegate
@@ -205,13 +205,14 @@ public class MagnificationControllerTest {
));
mScreenMagnificationController.register(TEST_DISPLAY);
- mWindowMagnificationManager = spy(new WindowMagnificationManager(mContext, globalLock,
- mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
+ mMagnificationConnectionManager = spy(
+ new MagnificationConnectionManager(mContext, globalLock,
+ mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
- mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider,
+ mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
ConcurrentUtils.DIRECT_EXECUTOR));
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -254,8 +255,10 @@ public class MagnificationControllerTest {
mCallbackArgumentCaptor.getValue().onResult(true);
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -297,8 +300,10 @@ public class MagnificationControllerTest {
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -316,7 +321,7 @@ public class MagnificationControllerTest {
// The first time is triggered when window mode is activated.
// The second time is triggered when activating the window mode again.
// The third time is triggered when the transition is completed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
}
@@ -330,7 +335,7 @@ public class MagnificationControllerTest {
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -350,7 +355,7 @@ public class MagnificationControllerTest {
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -386,11 +391,11 @@ public class MagnificationControllerTest {
mTransitionCallBack);
// Enable window magnification while animating.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
Float.NaN, Float.NaN, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -408,8 +413,10 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
mMockConnection.invokeCallbacks();
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -438,7 +445,7 @@ public class MagnificationControllerTest {
animate, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
@@ -564,7 +571,7 @@ public class MagnificationControllerTest {
mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
- verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ verify(mMagnificationConnectionManager).onDisplayRemoved(TEST_DISPLAY);
verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY);
}
@@ -573,7 +580,7 @@ public class MagnificationControllerTest {
mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID);
verify(mScreenMagnificationController).resetAllIfNeeded(false);
- verify(mWindowMagnificationManager).disableAllWindowMagnifiers();
+ verify(mMagnificationConnectionManager).disableAllWindowMagnifiers();
verify(mScaleProvider).onUserChanged(SECOND_USER_ID);
}
@@ -584,7 +591,7 @@ public class MagnificationControllerTest {
mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
@@ -594,11 +601,11 @@ public class MagnificationControllerTest {
// The first time is trigger when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -625,8 +632,8 @@ public class MagnificationControllerTest {
mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
- verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
- verify(mWindowMagnificationManager, never()).persistScale(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(newScale));
+ verify(mMagnificationConnectionManager, never()).persistScale(eq(TEST_DISPLAY));
}
@Test
@@ -669,7 +676,7 @@ public class MagnificationControllerTest {
assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
assertEquals(config.getScale(), actualConfig.getScale(), 0);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(config.getScale()));
}
@@ -677,11 +684,11 @@ public class MagnificationControllerTest {
public void onSourceBoundChanged_windowEnabled_notifyMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- reset(mWindowMagnificationManager);
+ reset(mMagnificationConnectionManager);
mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, TEST_RECT);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(DEFAULT_SCALE));
}
@@ -780,11 +787,11 @@ public class MagnificationControllerTest {
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -799,10 +806,11 @@ public class MagnificationControllerTest {
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager, times(2))
+ .removeMagnificationButton(eq(TEST_DISPLAY));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -816,7 +824,7 @@ public class MagnificationControllerTest {
public void deactivateWindowMagnification_windowActivated_triggerCallbackAndLogUsage()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
verify(mMagnificationController).onWindowMagnificationActivationState(
eq(TEST_DISPLAY), eq(false));
@@ -828,7 +836,7 @@ public class MagnificationControllerTest {
public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
mMagnificationController.setMagnificationFollowTypingEnabled(false);
- verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+ verify(mMagnificationConnectionManager).setMagnificationFollowTypingEnabled(eq(false));
verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
}
@@ -850,7 +858,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -867,7 +875,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -880,7 +888,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -895,7 +903,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -970,7 +978,8 @@ public class MagnificationControllerTest {
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- verify(mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
+ verify(mMagnificationConnectionManager)
+ .disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
}
@Test
@@ -983,11 +992,11 @@ public class MagnificationControllerTest {
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1001,11 +1010,11 @@ public class MagnificationControllerTest {
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1018,11 +1027,11 @@ public class MagnificationControllerTest {
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1035,11 +1044,11 @@ public class MagnificationControllerTest {
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1053,11 +1062,11 @@ public class MagnificationControllerTest {
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1071,9 +1080,9 @@ public class MagnificationControllerTest {
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1082,11 +1091,11 @@ public class MagnificationControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1100,11 +1109,11 @@ public class MagnificationControllerTest {
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when fullscreen mode activation state is updated.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1113,10 +1122,10 @@ public class MagnificationControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1126,8 +1135,8 @@ public class MagnificationControllerTest {
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1142,10 +1151,10 @@ public class MagnificationControllerTest {
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1163,10 +1172,10 @@ public class MagnificationControllerTest {
// The first time is triggered when window mode is activated.
// The second time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1174,9 +1183,9 @@ public class MagnificationControllerTest {
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1185,7 +1194,7 @@ public class MagnificationControllerTest {
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1260,17 +1269,17 @@ public class MagnificationControllerTest {
private void activateMagnifier(int displayId, int mode, float centerX, float centerY)
throws RemoteException {
- final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ final boolean windowMagnifying = mMagnificationConnectionManager.isWindowMagnifierEnabled(
displayId);
if (windowMagnifying) {
- mWindowMagnificationManager.disableWindowMagnification(displayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
mMockConnection.invokeCallbacks();
}
if (mode == MODE_FULLSCREEN) {
mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
centerX, centerY, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
}
@@ -1304,10 +1313,10 @@ public class MagnificationControllerTest {
}
private static class WindowMagnificationMgrCallbackDelegate implements
- WindowMagnificationManager.Callback {
- private WindowMagnificationManager.Callback mCallback;
+ MagnificationConnectionManager.Callback {
+ private MagnificationConnectionManager.Callback mCallback;
- public void setDelegate(WindowMagnificationManager.Callback callback) {
+ public void setDelegate(MagnificationConnectionManager.Callback callback) {
mCallback = callback;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 612a091a6b1b..c4be51f9ecbd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -92,7 +92,7 @@ public class WindowMagnificationGestureHandlerTest {
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockWindowMagnificationConnection mMockConnection;
private SpyWindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
private WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
@@ -104,23 +104,23 @@ public class WindowMagnificationGestureHandlerTest {
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
- mock(WindowMagnificationManager.Callback.class), mMockTrace,
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+ mock(MagnificationConnectionManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new SpyWindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
+ mContext, mMagnificationConnectionManager, mMockTrace, mMockCallback,
/** detectSingleFingerTripleTap= */ true, /** detectTwoFingerTripleTap= */ true,
/** detectShortcutTrigger= */ true, DISPLAY_0);
mMockWindowMagnificationGestureHandler =
mWindowMagnificationGestureHandler.getMockGestureHandler();
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
}
@After
public void tearDown() {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, true);
}
@Test
@@ -378,7 +378,7 @@ public class WindowMagnificationGestureHandlerTest {
}
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT: {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, false);
}
break;
case STATE_TWO_FINGERS_DOWN: {
@@ -423,7 +423,7 @@ public class WindowMagnificationGestureHandlerTest {
}
private boolean isWindowMagnifierEnabled(int displayId) {
- return mWindowMagnificationManager.isWindowMagnifierEnabled(displayId);
+ return mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
private static String stateToString(int state) {
@@ -495,13 +495,14 @@ public class WindowMagnificationGestureHandlerTest {
private final WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
SpyWindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap, boolean detectTwoFingerTripleTap,
boolean detectShortcutTrigger, int displayId) {
- super(context, windowMagnificationMgr, trace, callback, detectSingleFingerTripleTap,
- detectTwoFingerTripleTap, detectShortcutTrigger, displayId);
+ super(context, magnificationConnectionManager, trace, callback,
+ detectSingleFingerTripleTap, detectTwoFingerTripleTap,
+ detectShortcutTrigger, displayId);
mMockWindowMagnificationGestureHandler = mock(WindowMagnificationGestureHandler.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 749b07d16ebe..9c8276aac4dd 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -19,6 +19,16 @@ import static android.media.AudioManager.GET_DEVICES_OUTPUTS;
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
+
+import static junit.framework.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -34,11 +44,15 @@ import android.media.ILoudnessCodecUpdatesDispatcher;
import android.media.LoudnessCodecInfo;
import android.media.PlayerBase;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
+import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -84,8 +98,7 @@ public class LoudnessCodecHelperTest {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
verify(mDispatcher).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid), any());
}
@@ -96,8 +109,7 @@ public class LoudnessCodecHelperTest {
mLoudnessHelper.unregisterLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/false,
- CODEC_METADATA_TYPE_MPEG_D)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/false, CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(0)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -108,11 +120,9 @@ public class LoudnessCodecHelperTest {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
- mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
+ mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(2)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -124,11 +134,10 @@ public class LoudnessCodecHelperTest {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
+ List.of(getLoudnessInfo(/*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
- mLoudnessHelper.addLoudnessCodecInfo(newPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ mLoudnessHelper.addLoudnessCodecInfo(newPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -140,12 +149,10 @@ public class LoudnessCodecHelperTest {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_4)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4)));
//does not trigger dispatch since active apc list does not contain newPiid
mLoudnessHelper.startLoudnessCodecUpdates(newPiid,
- List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D)));
+ List.of(getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
@@ -157,9 +164,8 @@ public class LoudnessCodecHelperTest {
@Test
public void updateCodecParameters_noStartedPiids_noDispatch() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
- mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
- getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
- CODEC_METADATA_TYPE_MPEG_D));
+ mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid, /*mediaCodecHash=*/222,
+ getLoudnessInfo(/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_D));
mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
@@ -170,8 +176,8 @@ public class LoudnessCodecHelperTest {
@Test
public void updateCodecParameters_removedCodecInfo_noDispatch() throws Exception {
- final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
- /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+ final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+ CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -186,8 +192,8 @@ public class LoudnessCodecHelperTest {
@Test
public void updateCodecParameters_stoppedPiids_noDispatch() throws Exception {
- final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
- /*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
+ final LoudnessCodecInfo info = getLoudnessInfo(/*isDownmixing=*/true,
+ CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
@@ -200,6 +206,108 @@ public class LoudnessCodecHelperTest {
any());
}
+ @Test
+ public void checkParcelableBundle_forMpeg4CodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+ }
+
+ @Test
+ public void checkParcelableBundle_forMpegDCodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+ }
+
private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
@@ -220,11 +328,15 @@ public class LoudnessCodecHelperTest {
return apcList;
}
- private static LoudnessCodecInfo getLoudnessInfo(int mediaCodecHash, boolean isDownmixing,
- int metadataType) {
+ private static LoudnessCodecInputProperties createInputProperties(
+ int metadataType, boolean isDownmixing, @DeviceSplRange int splRange) {
+ return new LoudnessCodecInputProperties.Builder().setMetadataType(
+ metadataType).setIsDownmixing(isDownmixing).setDeviceSplRange(splRange).build();
+ }
+
+ private static LoudnessCodecInfo getLoudnessInfo(boolean isDownmixing, int metadataType) {
LoudnessCodecInfo info = new LoudnessCodecInfo();
info.isDownmixing = isDownmixing;
- info.mediaCodecHashCode = mediaCodecHash;
info.metadataType = metadataType;
return info;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index b5ba322b1a5e..9213601a6144 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -413,18 +413,24 @@ public class VirtualDeviceManagerServiceTest {
public void getDeviceIdForDisplayId_invalidDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_defaultDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
@@ -433,6 +439,8 @@ public class VirtualDeviceManagerServiceTest {
assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
.isEqualTo(mDeviceImpl.getDeviceId());
+ assertThat(mLocalService.getDeviceIdForDisplayId(DISPLAY_ID_1))
+ .isEqualTo(mDeviceImpl.getDeviceId());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 01922e08d71d..edfe1b416f22 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -40,6 +40,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Surface;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +78,11 @@ public class VirtualCameraControllerTest {
when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
}
+ @After
+ public void tearDown() throws Exception {
+ mVirtualCameraController.close();
+ }
+
@Test
public void registerCamera_registersCamera() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
@@ -95,6 +101,8 @@ public class VirtualCameraControllerTest {
public void unregisterCamera_unregistersCamera() throws Exception {
VirtualCameraConfig config = createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+ mVirtualCameraController.registerCamera(config);
+
mVirtualCameraController.unregisterCamera(config);
verify(mVirtualCameraServiceMock).unregisterCamera(any());
@@ -107,9 +115,10 @@ public class VirtualCameraControllerTest {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+ mVirtualCameraController.close();
+
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
- mVirtualCameraController.close();
verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
configurationCaptor.capture());
List<VirtualCameraConfiguration> virtualCameraConfigurations =
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
deleted file mode 100644
index 5aef7a320930..000000000000
--- a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.media;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
-import android.media.IAudioRoutesObserver;
-import android.media.MediaRoute2Info;
-import android.os.RemoteException;
-
-import com.android.internal.R;
-import com.android.server.audio.AudioService;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(JUnit4.class)
-public class AudioPoliciesDeviceRouteControllerTest {
-
- private static final String ROUTE_NAME_DEFAULT = "default";
- private static final String ROUTE_NAME_DOCK = "dock";
- private static final String ROUTE_NAME_HEADPHONES = "headphones";
-
- private static final int VOLUME_SAMPLE_1 = 25;
-
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private AudioManager mAudioManager;
- @Mock
- private AudioService mAudioService;
- @Mock
- private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
-
- @Captor
- private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
-
- private AudioPoliciesDeviceRouteController mController;
-
- private IAudioRoutesObserver.Stub mAudioRoutesObserver;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT);
-
- // Setting built-in speaker as default speaker.
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
- when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
- .thenReturn(audioRoutesInfo);
-
- mController = new AudioPoliciesDeviceRouteController(
- mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener);
-
- mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue();
- }
-
- @Test
- public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
- MediaRoute2Info route2Info = mController.getSelectedRoute();
-
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
- }
-
- @Test
- public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void getDeviceRoute_selectDevice_returnsSelectedRoute() {
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- }
-
- @Test
- public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- }
-
- @Test
- public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(null);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- }
-
- @Test
- public void selectRoute_selectWiredRoute_returnsTrue() {
- assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue();
- }
-
- @Test
- public void selectRoute_selectBluetoothRoute_returnsFalse() {
- assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse();
- }
-
- @Test
- public void selectRoute_unselectRoute_returnsTrue() {
- assertThat(mController.selectRoute(null)).isTrue();
- }
-
- @Test
- public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.updateVolume(VOLUME_SAMPLE_1);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
- assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
- }
-
- @Test
- public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() {
- when(mResources.getText(R.string.default_audio_route_name_headphones))
- .thenReturn(ROUTE_NAME_HEADPHONES);
- when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
- .thenReturn(ROUTE_NAME_DOCK);
-
- AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
- audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
- callAudioRoutesObserver(audioRoutesInfo);
-
- mController.updateVolume(VOLUME_SAMPLE_1);
-
- mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
-
- MediaRoute2Info route2Info = mController.getSelectedRoute();
- assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
- assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
- }
-
- /**
- * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)}
- * from {@link AudioService}. This happens when there is a wired route change,
- * like a wired headset being connected.
- *
- * @param audioRoutesInfo updated state of connected wired device
- */
- private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) {
- try {
- // this is a captured observer implementation
- // from WiredRoutesController's AudioService#startWatchingRoutes call
- mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo);
- } catch (RemoteException exception) {
- // Should not happen since the object is mocked.
- assertWithMessage("An unexpected RemoteException happened.").fail();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index 14b121d3945c..0961b7d97177 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.media;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
import android.content.Context;
+import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -56,7 +57,8 @@ public class DeviceRouteControllerTest {
@RequiresFlagsDisabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public void createInstance_audioPoliciesFlagIsDisabled_createsLegacyController() {
DeviceRouteController deviceRouteController =
- DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+ DeviceRouteController.createInstance(
+ mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
Truth.assertThat(deviceRouteController).isInstanceOf(LegacyDeviceRouteController.class);
}
@@ -65,7 +67,8 @@ public class DeviceRouteControllerTest {
@RequiresFlagsEnabled(FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER)
public void createInstance_audioPoliciesFlagIsEnabled_createsAudioPoliciesController() {
DeviceRouteController deviceRouteController =
- DeviceRouteController.createInstance(mContext, mOnDeviceRouteChangedListener);
+ DeviceRouteController.createInstance(
+ mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
Truth.assertThat(deviceRouteController)
.isInstanceOf(AudioPoliciesDeviceRouteController.class);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 6ae26585fab2..cd29c8057706 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -24,11 +24,13 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
+import android.app.usage.Flags;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.LongSparseArray;
@@ -183,6 +185,17 @@ public class UsageStatsDatabaseTest {
case Event.LOCUS_ID_SET:
event.mLocusId = "locus" + (i % 7); //"random" locus
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ // "random" user interaction extras.
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ "fake.namespace.category" + (i % 13));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION,
+ "fakeaction" + (i % 13));
+ event.mExtras = extras;
+ }
+ break;
}
mIntervalStats.addEvent(event);
@@ -295,6 +308,18 @@ public class UsageStatsDatabaseTest {
assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
"Usage event " + debugId);
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ PersistableBundle extras1 = e1.getExtras();
+ PersistableBundle extras2 = e2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ "Usage event " + debugId);
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ "Usage event " + debugId);
+ }
+ break;
}
// fallthrough
case 4: // test fields added in version 4
diff --git a/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
new file mode 100644
index 000000000000..377e4c3d6810
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link UserSettingDeviceConfigMediator}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UserSettingDeviceConfigMediatorTest {
+ @Test
+ public void testDeviceConfigOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 1)
+ .setFloat("float", .5f)
+ .setBoolean("boolean", true)
+ .setLong("long", 123456789)
+ .setString("string", "abc123")
+ .build();
+
+ mediator.setDeviceConfigProperties(properties);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the properties
+ mediator.setDeviceConfigProperties(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123";
+
+ mediator.setSettingsString(settings);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the settings
+ mediator.setSettingsString(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOverridesAll() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides all, anything in DeviceConfig should be ignored,
+ // even if settings doesn't have a value for it.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(123, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString("");
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString(null);
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+
+ @Test
+ public void testSettingsOverridesIndividual() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides individual, anything in DeviceConfig that doesn't exist in
+ // settings should be used.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index d00060564e74..32082e3d857e 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -28,6 +28,7 @@ import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.suitebuilder.annotation.MediumTest;
@@ -1449,4 +1450,21 @@ public class WebViewUpdateServiceTest {
checkPreparationPhasesForPackage(currentSdkPackage.packageName,
1 /* first preparation phase */);
}
+
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
+ String nonDefaultPackage = "nonDefaultPackage";
+ String defaultPackage1 = "defaultPackage1";
+ String defaultPackage2 = "defaultPackage2";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+ new WebViewProviderInfo(defaultPackage1, "", true, false, null),
+ new WebViewProviderInfo(defaultPackage2, "", true, false, null)
+ };
+ setupWithPackages(packages);
+ assertEquals(
+ defaultPackage1, mWebViewUpdateServiceImpl.getDefaultWebViewPackage().packageName);
+ }
}
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 09ffe71a6758..ee08fd26d631 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -28,6 +28,7 @@ import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -320,6 +321,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final int SECONDARY_DISPLAY_ID = 42;
private static final int TEST_PROFILE_USERHANDLE = 12;
+ private static final String ACTION_NOTIFICATION_TIMEOUT =
+ NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
+ private static final String EXTRA_KEY = "key";
+ private static final String SCHEME_TIMEOUT = "timeout";
+
private final int mUid = Binder.getCallingUid();
private final @UserIdInt int mUserId = UserHandle.getUserId(mUid);
@@ -442,6 +448,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
BroadcastReceiver mUserSwitchIntentReceiver;
+ BroadcastReceiver mNotificationTimeoutReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
@@ -677,6 +684,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
any(), intentFilterCaptor.capture(), any(), any());
verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
+ intentFilterCaptor.capture(), anyInt());
+ verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture());
List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
@@ -695,9 +704,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mUserSwitchIntentReceiver = broadcastReceivers.get(i);
}
}
+ if (filter.hasAction(ACTION_NOTIFICATION_TIMEOUT)
+ && filter.hasDataScheme(SCHEME_TIMEOUT)) {
+ mNotificationTimeoutReceiver = broadcastReceivers.get(i);
+ }
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
assertNotNull("User-switch receiver should exist", mUserSwitchIntentReceiver);
+ assertNotNull("Notification timeout receiver should exist", mNotificationTimeoutReceiver);
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -2430,6 +2444,59 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testCancelWithTagDoesNotCancelLifetimeExtended() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final NotificationRecord notif = generateNotificationRecord(null);
+ notif.getSbn().getNotification().flags =
+ Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+ final StatusBarNotification sbn = notif.getSbn();
+
+ assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+ mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+
+ assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+ mSetFlagsRule.disableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+
+ assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(0);
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testCancelAllDoesNotCancelLifetimeExtended() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ // Adds a lifetime extended notification.
+ final NotificationRecord notif = generateNotificationRecord(mTestNotificationChannel, 1,
+ null, false);
+ notif.getSbn().getNotification().flags =
+ Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+ // Adds a second, non-lifetime extended notification.
+ final NotificationRecord notifCancelable = generateNotificationRecord(
+ mTestNotificationChannel, 2, null, false);
+ mService.addNotification(notifCancelable);
+ // Verify that both notifications have been posted and are active.
+ assertThat(mBinderService.getActiveNotifications(PKG).length).isEqualTo(2);
+
+ mBinderService.cancelAllNotifications(PKG, notif.getSbn().getUserId());
+ waitForIdle();
+
+ // The non-lifetime extended notification, with id = 2, has been cancelled.
+ StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifs.length).isEqualTo(1);
+ assertThat(notifs[0].getId()).isEqualTo(1);
+ }
+
+ @Test
public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
throws Exception {
when(mAmi.applyForegroundServiceNotification(
@@ -2832,6 +2899,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testCancelNotificationsFromListener_clearAll_NoClearLifetimeExt()
+ throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, false);
+ notif.getNotification().flags = FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
+ assertThat(notifs.length).isEqualTo(1);
+ }
+
+ @Test
public void testCancelNotificationsFromListener_byKey_GroupWithOngoingParent()
throws Exception {
final NotificationRecord parent = generateNotificationRecord(
@@ -3036,6 +3121,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testCancelNotificationsFromListener_byKey_NoClearLifetimeExt()
+ throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 3, null, false);
+ notif.getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+ String[] keys = {notif.getSbn().getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
public void testGroupInstanceIds() throws Exception {
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
@@ -5298,6 +5399,79 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
anyInt());
}
+ private void simulateNotificationTimeoutBroadcast(String notificationKey) {
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRA_KEY, notificationKey);
+ final Intent intent = new Intent(ACTION_NOTIFICATION_TIMEOUT);
+ intent.putExtras(extras);
+ mNotificationTimeoutReceiver.onReceive(getContext(), intent);
+ }
+
+ @Test
+ public void testTimeout_CancelsNotification() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, false);
+ mService.addNotification(notif);
+
+ simulateNotificationTimeoutBroadcast(notif.getKey());
+ waitForIdle();
+
+ // Check that the notification was cancelled.
+ StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifsAfter.length).isEqualTo(0);
+ assertThat(mService.getNotificationRecord(notif.getKey())).isNull();
+ }
+
+ @Test
+ public void testTimeout_NoCancelForegroundServiceNotification() throws Exception {
+ // Creates a notification with FLAG_FOREGROUND_SERVICE
+ final NotificationRecord notif = generateNotificationRecord(null);
+ notif.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+ mService.addNotification(notif);
+
+ simulateNotificationTimeoutBroadcast(notif.getKey());
+ waitForIdle();
+
+ // Check that the notification was not cancelled.
+ StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifsAfter.length).isEqualTo(1);
+ assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+ }
+
+ @Test
+ public void testTimeout_NoCancelUserInitJobNotification() throws Exception {
+ // Create a notification with FLAG_USER_INITIATED_JOB
+ final NotificationRecord notif = generateNotificationRecord(null);
+ notif.getSbn().getNotification().flags = Notification.FLAG_USER_INITIATED_JOB;
+ mService.addNotification(notif);
+
+ simulateNotificationTimeoutBroadcast(notif.getKey());
+ waitForIdle();
+
+ // Check that the notification was not cancelled.
+ StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifsAfter.length).isEqualTo(1);
+ assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+ }
+
+ @Test
+ public void testTimeout_NoCancelLifetimeExtensionNotification() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ // Create a notification with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
+ final NotificationRecord notif = generateNotificationRecord(null);
+ notif.getSbn().getNotification().flags =
+ Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+
+ simulateNotificationTimeoutBroadcast(notif.getKey());
+ waitForIdle();
+
+ // Check that the notification was not cancelled.
+ StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
+ assertThat(notifsAfter.length).isEqualTo(1);
+ assertThat(mService.getNotificationRecord(notif.getKey())).isEqualTo(notif);
+ }
+
@Test
public void testBumpFGImportance_channelChangePreOApp() throws Exception {
String preOPkg = PKG_N_MR1;
@@ -7913,6 +8087,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testOnNotificationSmartReplySent() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
final int replyIndex = 2;
final String reply = "Hello";
final boolean modifiedBeforeSending = true;
@@ -7930,6 +8105,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(1, mNotificationRecordLogger.numCalls());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
mNotificationRecordLogger.event(0));
+ // Check that r.recordSmartReplied was called.
+ assertThat(r.getSbn().getNotification().flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY)
+ .isGreaterThan(0);
+ assertThat(r.getStats().hasSmartReplied()).isTrue();
}
@Test
@@ -13116,6 +13295,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
eq("package"), anyString(), anyInt(), anyBoolean());
}
+ @Test
+ public void testFixNotification_clearsLifetimeExtendedFlag() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ Notification n = new Notification.Builder(mContext, "test")
+ .setFlag(FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
+ .build();
+
+ assertThat(n.flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
+
+ mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
+
+ assertThat(n.flags & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index f83a1df358bd..670d09795e12 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -29,6 +29,8 @@ import static android.service.notification.NotificationListenerService.Ranking.U
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -44,6 +46,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.Flags;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -64,6 +67,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.Vibrator;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
@@ -80,6 +84,7 @@ import com.android.server.UiServiceTestCase;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -122,6 +127,9 @@ public class NotificationRecordTest extends UiServiceTestCase {
private static final NotificationRecord.Light CUSTOM_LIGHT =
new NotificationRecord.Light(1, 2, 3);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -651,6 +659,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
@Test
public void testNotificationStats() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /* defaultLights */, groupId /* group */);
@@ -690,6 +699,37 @@ public class NotificationRecordTest extends UiServiceTestCase {
record.recordDirectReplied();
assertTrue(record.getStats().hasDirectReplied());
+
+ record.recordSmartReplied();
+ assertThat(record.getStats().hasSmartReplied()).isTrue();
+ }
+
+ @Test
+ public void testDirectRepliedAddsLifetimeExtensionFlag() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ record.recordDirectReplied();
+ assertThat(record.getSbn().getNotification().flags
+ & Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
+ }
+
+ @Test
+ public void testSmartRepliedAddsLifetimeExtensionFlag() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ record.recordSmartReplied();
+ assertThat(record.getSbn().getNotification().flags
+ & Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isGreaterThan(0);
}
@Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f2a1fe859634..c3074bb0fee8 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -93,8 +93,6 @@
android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
- <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
-
<activity android:name="com.android.server.wm.SurfaceSyncGroupTests$TestActivity"
android:screenOrientation="locked"
android:turnScreenOn="true"
@@ -122,6 +120,13 @@
<activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity"
android:exported="true">
</activity>
+
+ <activity android:name="com.android.server.wm.utils.TestActivity"
+ android:screenOrientation="locked"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true" />
</application>
<instrumentation
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index f8ebeaddcb7e..46e87dceb8d4 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -30,4 +30,8 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+ </target_preparer>
</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 2c35cf0c4151..8d236eda5dc5 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -110,24 +110,6 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
}
/**
- * META + SPACE to switch keyboard layout.
- */
- @Test
- public void testMetaSpace() {
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SPACE}, 0);
- mPhoneWindowManager.assertSwitchKeyboardLayout(1);
- }
-
- /**
- * META + SHIFT + SPACE to switch keyboard layout backwards.
- */
- @Test
- public void testMetaShiftSpace() {
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0);
- mPhoneWindowManager.assertSwitchKeyboardLayout(-1);
- }
-
- /**
* CTRL + ALT + Z to enable accessibility service.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 71098aa5e883..360fdf3ae3f7 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -132,13 +132,6 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase {
{"LANGUAGE_SWITCH key -> Switch Keyboard Language",
new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
- {"Meta + Space -> Switch Keyboard Language",
- new int[]{META_KEY, KeyEvent.KEYCODE_SPACE},
- KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON},
- {"Meta + Shift + Space -> Switch Keyboard Language",
- new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE},
- KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE,
- META_ON | SHIFT_ON},
{"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON},
{"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 9f584911aed7..9efbe3587ab2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,8 +25,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static com.android.server.wm.utils.LastCallVerifier.lastCall;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,11 +34,14 @@ import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import com.android.server.testutils.StubTransaction;
import com.android.server.wm.utils.MockAnimationAdapter;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -122,7 +123,8 @@ public class DimmerTests extends WindowTestsBase {
}
}
- static class MockAnimationAdapterFactory extends SmoothDimmer.AnimationAdapterFactory {
+ static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory {
+ @Override
public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
SurfaceAnimationRunner runner) {
return sTestAnimation;
@@ -175,8 +177,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
final int blur = 50;
mHost.addChild(mChild, 0);
@@ -195,8 +197,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, alpha, 20);
@@ -210,8 +212,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -230,8 +232,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -290,8 +292,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 2);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -310,8 +312,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 0);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -330,8 +332,8 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimmerWithBlurUpdatesTransaction_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final int blurRadius = 50;
@@ -344,4 +346,120 @@ public class DimmerTests extends WindowTestsBase {
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
+
+ /**
+ * mChild is requesting the dim values to be set directly. In this case, dim won't play the
+ * standard animation, but directly apply mChild's requests to the dim surface
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsOpeningAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.3f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(mTransaction).setAlpha(dimLayer, 0.2f);
+ verify(mTransaction).setAlpha(dimLayer, 0.3f);
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ }
+
+ /**
+ * Same as testContainerDimsOpeningAnimationByItself, but this is a more specific case in which
+ * alpha is animated to 0. This corner case is needed to verify that the layer is removed anyway
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsClosingAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).remove(dimLayer);
+ }
+
+ /**
+ * Check the handover of the dim between two windows and the consequent dim animation in between
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingConsecutively() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(2)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
+
+ /**
+ * Two windows are trying to modify the dim at the same time, but only the last request before
+ * updateDims will be satisfied
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingAtTheSameTime() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index 8119fd486a87..3b9ed2652610 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -23,15 +23,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertTrue;
-import android.app.Activity;
import android.app.Instrumentation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.Gravity;
import android.view.IWindow;
import android.view.SurfaceControl;
@@ -46,12 +45,12 @@ import android.widget.Button;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
+import com.android.server.wm.utils.TestActivity;
import org.junit.After;
import org.junit.Before;
@@ -59,11 +58,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@RunWith(WindowTestRunner.class)
public class SurfaceControlViewHostTests {
+ private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
private static final String TAG = "SurfaceControlViewHostTests";
private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
@@ -76,6 +78,8 @@ public class SurfaceControlViewHostTests {
private SurfaceControlViewHost mScvh1;
private SurfaceControlViewHost mScvh2;
+ private SurfaceView mSurfaceView;
+
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -96,15 +100,17 @@ public class SurfaceControlViewHostTests {
mView1 = new Button(mActivity);
mView2 = new Button(mActivity);
- mInstrumentation.runOnMainSync(() -> {
- try {
- mActivity.attachToSurfaceView(sc);
- } catch (InterruptedException e) {
- }
+ CountDownLatch svReadyLatch = new CountDownLatch(1);
+ mActivity.runOnUiThread(() -> addSurfaceView(svReadyLatch));
+ assertTrue("Failed to wait for SV to get created",
+ svReadyLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+ .show(sc).apply();
+ mInstrumentation.runOnMainSync(() -> {
TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
mActivity.getResources().getConfiguration(), sc,
- mActivity.mSurfaceView.getHostToken());
+ mSurfaceView.getHostToken());
mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
wwm, "requestFocusWithMultipleWindows");
@@ -135,7 +141,7 @@ public class SurfaceControlViewHostTests {
}
assertTrue("Failed to wait for view2", wasVisible);
- IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
+ IWindow window = IWindow.Stub.asInterface(mSurfaceView.getWindowToken());
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh1.getInputTransferToken(), true);
@@ -162,43 +168,30 @@ public class SurfaceControlViewHostTests {
}
}
- public static class TestActivity extends Activity implements SurfaceHolder.Callback {
- private SurfaceView mSurfaceView;
- private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+ private void addSurfaceView(CountDownLatch svReadyLatch) {
+ final FrameLayout content = mActivity.getParentLayout();
+ mSurfaceView = new SurfaceView(mActivity);
+ mSurfaceView.setZOrderOnTop(true);
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+ Gravity.LEFT | Gravity.TOP);
+ mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ svReadyLatch.countDown();
+ }
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final FrameLayout content = new FrameLayout(this);
- mSurfaceView = new SurfaceView(this);
- mSurfaceView.setBackgroundColor(Color.BLACK);
- mSurfaceView.setZOrderOnTop(true);
- final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
- Gravity.LEFT | Gravity.TOP);
- content.addView(mSurfaceView, lp);
- setContentView(content);
- mSurfaceView.getHolder().addCallback(this);
- }
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSvReadyLatch.countDown();
- }
+ }
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
- int height) {
- }
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- }
-
- public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
- mSvReadyLatch.await();
- new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
- .show(sc).apply();
- }
+ }
+ });
+ content.addView(mSurfaceView, lp);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index 3cb4a1d7b7ec..e65a9feed1aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -19,6 +19,8 @@ package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import android.os.Handler;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
@@ -39,6 +41,9 @@ class SystemServiceTestsBase {
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
this::onBeforeSystemServicesCreated);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@WindowTestRunner.MethodWrapperRule
public final WindowManagerGlobalLockRule mLockRule =
new WindowManagerGlobalLockRule(mSystemServicesTestRule);
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 b1def8d0b3a6..51f0404e2396 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -444,7 +444,9 @@ public class SystemServicesTestRule implements TestRule {
SurfaceAnimationThread.dispose();
AnimationThread.dispose();
UiThread.dispose();
- mInputChannel.dispose();
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ }
tearDownLocalServices();
// Reset priority booster because animation thread has been changed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 8a90f127f4eb..06f29c262b42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -25,7 +25,9 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
@@ -1759,6 +1761,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
+ public void testApplyTransaction_createTaskFragmentDecorSurface() {
+ // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+
+ final TaskFragment tf = createTaskFragment(task);
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_CREATE_TASK_FRAGMENT_DECOR_SURFACE).build();
+ mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+ assertApplyTransactionAllowed(mTransaction);
+
+ verify(task).moveOrCreateDecorSurfaceFor(tf);
+ }
+
+ @Test
+ public void testApplyTransaction_removeTaskFragmentDecorSurface() {
+ // TODO(b/293654166) remove system organizer requirement once security review is cleared.
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf = createTaskFragment(task);
+
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE).build();
+ mTransaction.addTaskFragmentOperation(tf.getFragmentToken(), operation);
+
+ assertApplyTransactionAllowed(mTransaction);
+
+ verify(task).removeDecorSurface();
+ }
+
+ @Test
public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
@@ -1966,7 +2002,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
/** Setups the mock Task as the parent of the given TaskFragment. */
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
- doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true))
+ doReturn(new TaskFragmentParentInfo(
+ new Configuration(), DEFAULT_DISPLAY, true, true, null /* decorSurface */))
.when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 5e531b4cbc4f..da7612b17dc9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -63,6 +63,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.app.ActivityManager;
@@ -82,6 +83,7 @@ import android.util.DisplayMetrics;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.SurfaceControl;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -1619,6 +1621,185 @@ public class TaskTests extends WindowTestsBase {
assertFalse(task.isDragResizing());
}
+ @Test
+ public void testMoveOrCreateDecorSurface() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+ // Decor surface should not be present initially.
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+
+ // Decor surface should be created.
+ clearInvocations(task);
+ task.moveOrCreateDecorSurfaceFor(fragment);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ assertNotNull(task.getTaskFragmentParentInfo().getDecorSurface());
+ assertEquals(fragment, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Decor surface should be removed.
+ clearInvocations(task);
+ task.removeDecorSurface();
+
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ verify(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ assertNull(task.getTaskFragmentParentInfo().getDecorSurface());
+ }
+
+ @Test
+ public void testMoveOrCreateDecorSurface_whenOwnerTaskFragmentRemoved() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+
+ task.moveOrCreateDecorSurfaceFor(fragment1);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ assertEquals(fragment1, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Transfer ownership
+ task.moveOrCreateDecorSurfaceFor(fragment2);
+
+ assertNotNull(task.mDecorSurfaceContainer);
+ assertNotNull(task.getDecorSurface());
+ assertEquals(fragment2, task.mDecorSurfaceContainer.mOwnerTaskFragment);
+
+ // Safe surface should be removed when the owner TaskFragment is removed.
+ task.removeChild(fragment2);
+
+ verify(task).removeDecorSurface();
+ assertNull(task.mDecorSurfaceContainer);
+ assertNull(task.getDecorSurface());
+ }
+
+ @Test
+ public void testAssignChildLayers_decorSurfacePlacement() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord unembeddedActivity = task.getTopMostActivity();
+
+ final TaskFragment fragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
+ spyOn(unembeddedActivity);
+ spyOn(fragment1);
+ spyOn(fragment2);
+
+ // Initially, the decor surface should not be placed.
+ task.assignChildLayers(t);
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(fragment2).assignLayer(t, 2);
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed just above the owner TaskFragment.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.moveOrCreateDecorSurfaceFor(fragment1);
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be invisible if the owner TaskFragment is invisible.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(false).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, false);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed on below activity from a different UID.
+ doReturn(false).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 0);
+ verify(unembeddedActivity).assignLayer(t, 1);
+ verify(fragment1).assignLayer(t, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should be placed below untrusted embedded TaskFragment.
+ doReturn(true).when(unembeddedActivity).isUid(task.effectiveUid);
+ doReturn(true).when(fragment1).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(false).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
+ doReturn(true).when(fragment1).isVisible();
+
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(t).setLayer(task.mDecorSurfaceContainer.mContainerSurface, 2);
+ verify(fragment2).assignLayer(t, 3);
+ verify(t).setVisibility(task.mDecorSurfaceContainer.mContainerSurface, true);
+ verify(t, never()).setLayer(eq(task.getDecorSurface()), anyInt());
+
+ clearInvocations(t);
+ clearInvocations(unembeddedActivity);
+ clearInvocations(fragment1);
+ clearInvocations(fragment2);
+
+ // The decor surface should not be placed after removal.
+ task.removeDecorSurface();
+ task.assignChildLayers(t);
+
+ verify(unembeddedActivity).assignLayer(t, 0);
+ verify(fragment1).assignLayer(t, 1);
+ verify(fragment2).assignLayer(t, 2);
+ }
+
private Task getTestTask() {
return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}
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 dade3b91e0eb..71447e72de8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2015,6 +2015,9 @@ public class TransitionTests extends WindowTestsBase {
transition.collect(leafTaskA);
rootTaskA.moveToFront("test", leafTaskA);
+ // Test has order changes, a shallow check of order changes
+ assertTrue(transition.hasOrderChanges());
+
// All the tasks were already visible, so there shouldn't be any changes
ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
participants, changes);
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
new file mode 100644
index 000000000000..c12dcddd1b36
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * TestActivity that will ensure it dismisses keyguard and shows as a fullscreen activity.
+ */
+public class TestActivity extends Activity {
+ private static final int sTypeMask = systemBars() | displayCutout();
+ private FrameLayout mParentLayout;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mParentLayout = new FrameLayout(this);
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(mParentLayout, layoutParams);
+
+ WindowInsetsController windowInsetsController = getWindow().getInsetsController();
+ windowInsetsController.hide(sTypeMask);
+ WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ getWindow().setAttributes(params);
+ getWindow().setDecorFitsSystemWindows(false);
+
+ final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService(
+ KeyguardManager.class);
+ if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
+ keyguardManager.requestDismissKeyguard(this, null);
+ }
+ }
+
+ public FrameLayout getParentLayout() {
+ return mParentLayout;
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index bfb159f766d5..dce4818e8ed2 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -31,6 +31,7 @@ import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -42,7 +43,9 @@ import android.app.usage.EventList;
import android.app.usage.EventStats;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -575,6 +578,23 @@ public class IntervalStats {
continue;
}
break;
+ case USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ String category = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mCategoryToken);
+ String action = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mActionToken);
+ if (TextUtils.isEmpty(category) || TextUtils.isEmpty(action)) {
+ this.events.remove(i);
+ dataOmitted = true;
+ continue;
+ }
+ event.mExtras = new PersistableBundle();
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, category);
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+ event.mUserInteractionExtrasToken = null;
+ }
+ break;
}
}
if (dataOmitted) {
@@ -692,13 +712,30 @@ public class IntervalStats {
event.mPackage, event.mLocusId);
}
break;
+ case USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ final String category = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ final String action = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (!TextUtils.isEmpty(category) && !TextUtils.isEmpty(action)) {
+ event.mUserInteractionExtrasToken =
+ new Event.UserInteractionEventExtrasToken();
+ event.mUserInteractionExtrasToken.mCategoryToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ category);
+ event.mUserInteractionExtrasToken.mActionToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ action);
+ }
+ }
+ break;
}
}
}
/**
* Obfuscates the data in this instance of interval stats.
- *
* @hide
*/
public void obfuscateData(PackagesTokenData packagesTokenData) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index 81387471f4d6..d86534582358 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -17,8 +17,10 @@ package com.android.server.usage;
import android.app.usage.ConfigurationStats;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event.UserInteractionEventExtrasToken;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -26,6 +28,8 @@ import android.util.SparseIntArray;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -282,6 +286,16 @@ final class UsageStatsProtoV2 {
event.mLocusIdToken = proto.readInt(
EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
break;
+ case (int) EventObfuscatedProto.INTERACTION_EXTRAS:
+ try {
+ final long interactionExtrasToken = proto.start(
+ EventObfuscatedProto.INTERACTION_EXTRAS);
+ event.mUserInteractionExtrasToken = parseUserInteractionEventExtras(proto);
+ proto.end(interactionExtrasToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some user interaction extras from proto.", e);
+ }
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
}
@@ -386,7 +400,7 @@ final class UsageStatsProtoV2 {
}
private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
- final UsageEvents.Event event) throws IllegalArgumentException {
+ final UsageEvents.Event event) throws IOException, IllegalArgumentException {
proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
@@ -429,6 +443,12 @@ final class UsageStatsProtoV2 {
event.mNotificationChannelIdToken + 1);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ writeUserInteractionEventExtras(proto, EventObfuscatedProto.INTERACTION_EXTRAS,
+ event.mUserInteractionExtrasToken);
+ }
+ break;
}
}
@@ -703,6 +723,9 @@ final class UsageStatsProtoV2 {
case (int) PendingEventProto.TASK_ROOT_CLASS:
event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
break;
+ case (int) PendingEventProto.EXTRAS:
+ event.mExtras = parsePendingEventExtras(proto, PendingEventProto.EXTRAS);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
@@ -757,7 +780,7 @@ final class UsageStatsProtoV2 {
}
private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
if (event.mClass != null) {
proto.write(PendingEventProto.CLASS_NAME, event.mClass);
@@ -794,6 +817,11 @@ final class UsageStatsProtoV2 {
event.mNotificationChannelId);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ writePendingEventExtras(proto, PendingEventProto.EXTRAS, event.mExtras);
+ }
+ break;
}
}
@@ -888,4 +916,52 @@ final class UsageStatsProtoV2 {
proto.end(token);
}
}
+
+ private static UserInteractionEventExtrasToken parseUserInteractionEventExtras(
+ ProtoInputStream proto) throws IOException {
+ UserInteractionEventExtrasToken interactionExtrasToken =
+ new UserInteractionEventExtrasToken();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN:
+ interactionExtrasToken.mCategoryToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN) - 1;
+ break;
+ case (int) ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN:
+ interactionExtrasToken.mActionToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN) - 1;
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return interactionExtrasToken;
+ }
+ }
+ }
+
+ static void writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId,
+ UserInteractionEventExtrasToken interactionExtras) {
+ final long token = proto.start(fieldId);
+ proto.write(ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN,
+ interactionExtras.mCategoryToken + 1);
+ proto.write(ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN,
+ interactionExtras.mActionToken + 1);
+ proto.end(token);
+ }
+
+ /**
+ * Populates the extra details for pending interaction event from the protobuf stream.
+ */
+ private static PersistableBundle parsePendingEventExtras(ProtoInputStream proto, long fieldId)
+ throws IOException {
+ return PersistableBundle.readFromStream(new ByteArrayInputStream(proto.readBytes(fieldId)));
+ }
+
+ /**
+ * Write the extra details for pending interaction event to a protobuf stream.
+ */
+ static void writePendingEventExtras(ProtoOutputStream proto, long fieldId,
+ PersistableBundle eventExtras) throws IOException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ eventExtras.writeToStream(baos);
+ proto.write(fieldId, baos.toByteArray());
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4c56f33af430..0e1e0c8e3ad3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -83,6 +83,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -91,6 +92,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -193,6 +195,11 @@ public class UsageStatsService extends SystemService implements
private static final char TOKEN_DELIMITER = '/';
+ // The maximum length for extras {@link UsageStatsManager#EXTRA_EVENT_CATEGORY},
+ // {@link UsageStatsManager#EXTRA_EVENT_ACTION} in a {@link UsageEvents.Event#mExtras}.
+ // The value will be truncated at this limit.
+ private static final int MAX_TEXT_LENGTH = 127;
+
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
@@ -1814,6 +1821,13 @@ public class UsageStatsService extends SystemService implements
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
+ private String getTrimmedString(String input) {
+ if (input != null && input.length() > MAX_TEXT_LENGTH) {
+ return input.substring(0, MAX_TEXT_LENGTH);
+ }
+ return input;
+ }
+
/**
* Called by the Binder stub.
*/
@@ -2253,6 +2267,32 @@ public class UsageStatsService extends SystemService implements
}
}
+ private void reportUserInteractionInnerHelper(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
+ }
+ } else {
+ if (!isCallingUidSystem()) {
+ throw new SecurityException("Only system is allowed to call"
+ + " reportUserInteraction");
+ }
+ }
+
+ // Verify if this package exists before reporting an event for it.
+ if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
+ throw new IllegalArgumentException("Package " + packageName + "not exist!");
+ }
+
+ final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = packageName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
long endTime, String callingPackage, int userId) {
@@ -2686,23 +2726,36 @@ public class UsageStatsService extends SystemService implements
@Override
public void reportUserInteraction(String packageName, int userId) {
+ reportUserInteractionInnerHelper(packageName, userId, null);
+ }
+
+ @Override
+ public void reportUserInteractionWithBundle(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
Objects.requireNonNull(packageName);
- if (Flags.reportUsageStatsPermission()) {
- if (!canReportUsageStats()) {
- throw new SecurityException(
- "Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportUserInteraction");
- }
- } else {
- if (!isCallingUidSystem()) {
- throw new SecurityException("Only system is allowed to call"
- + " reportUserInteraction");
- }
+ if (extras == null || extras.size() == 0) {
+ throw new IllegalArgumentException("Emtry extras!");
}
- final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
- event.mPackage = packageName;
- reportEventOrAddToQueue(userId, event);
+ // Only category/action are allowed now, other unknown keys will be trimmed.
+ // Also, empty category/action is not meanful.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ if (TextUtils.isEmpty(category)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ }
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_ACTION);
+ }
+
+ PersistableBundle extrasCopy = new PersistableBundle();
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+
+ reportUserInteractionInnerHelper(packageName, userId, extrasCopy);
}
@Override
@@ -3160,6 +3213,24 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ if (extras != null && extras.size() != 0) {
+ // Truncate the value if necessary.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+ }
+
+ Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = pkgName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
+ @Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
userId, SystemClock.elapsedRealtime());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b67ab6d99c1..3bc77526a967 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1110,6 +1110,10 @@ class UserUsageStatsService {
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
+
+ if ((event.mEventType == Event.USER_INTERACTION) && (event.mExtras != null)) {
+ pw.print(event.mExtras.toString());
+ }
pw.printHexPair("flags", event.mFlags);
pw.println();
}
diff --git a/telephony/java/android/telephony/CellularIdentifierDisclosure.java b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
new file mode 100644
index 000000000000..7b2db6d59819
--- /dev/null
+++ b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A single occurrence of a cellular identifier being disclosed in the clear before a security
+ * context is established.
+ *
+ * @hide
+ */
+public final class CellularIdentifierDisclosure implements Parcelable {
+ private static final String TAG = "CellularIdentifierDisclosure";
+
+ private @NasProtocolMessage int mNasProtocolMessage;
+ private @CellularIdentifier int mCellularIdentifier;
+ private String mPlmn;
+ private boolean mIsEmergency;
+
+ public CellularIdentifierDisclosure(@NasProtocolMessage int nasProtocolMessage,
+ @CellularIdentifier int cellularIdentifier, String plmn, boolean isEmergency) {
+ mNasProtocolMessage = nasProtocolMessage;
+ mCellularIdentifier = cellularIdentifier;
+ mPlmn = plmn;
+ mIsEmergency = isEmergency;
+ }
+
+ private CellularIdentifierDisclosure(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public @NasProtocolMessage int getNasProtocolMessage() {
+ return mNasProtocolMessage;
+ }
+
+ public @CellularIdentifier int getCellularIdentifier() {
+ return mCellularIdentifier;
+ }
+
+ public String getPlmn() {
+ return mPlmn;
+ }
+
+ public boolean isEmergency() {
+ return mIsEmergency;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mNasProtocolMessage);
+ out.writeInt(mCellularIdentifier);
+ out.writeBoolean(mIsEmergency);
+ out.writeString8(mPlmn);
+ }
+
+ public static final Parcelable.Creator<CellularIdentifierDisclosure> CREATOR =
+ new Parcelable.Creator<CellularIdentifierDisclosure>() {
+ public CellularIdentifierDisclosure createFromParcel(Parcel in) {
+ return new CellularIdentifierDisclosure(in);
+ }
+
+ public CellularIdentifierDisclosure[] newArray(int size) {
+ return new CellularIdentifierDisclosure[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return TAG + ":{ mNasProtocolMessage = " + mNasProtocolMessage
+ + " mCellularIdentifier = " + mCellularIdentifier + " mIsEmergency = "
+ + mIsEmergency + " mPlmn = " + mPlmn;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CellularIdentifierDisclosure)) return false;
+ CellularIdentifierDisclosure that = (CellularIdentifierDisclosure) o;
+ return mNasProtocolMessage == that.mNasProtocolMessage
+ && mCellularIdentifier == that.mCellularIdentifier
+ && mIsEmergency == that.mIsEmergency && mPlmn.equals(that.mPlmn);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNasProtocolMessage, mCellularIdentifier, mIsEmergency,
+ mPlmn);
+ }
+
+ private void readFromParcel(@NonNull Parcel in) {
+ mNasProtocolMessage = in.readInt();
+ mCellularIdentifier = in.readInt();
+ mIsEmergency = in.readBoolean();
+ mPlmn = in.readString8();
+ }
+
+ public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0;
+ public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1;
+ public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2;
+ public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3;
+ public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4;
+ public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5;
+ public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6;
+ public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7;
+ public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8;
+ public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9;
+ public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10;
+ public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NAS_PROTOCOL_MESSAGE_"}, value = {NAS_PROTOCOL_MESSAGE_UNKNOWN,
+ NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
+ NAS_PROTOCOL_MESSAGE_DETACH_REQUEST, NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST,
+ NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST,
+ NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
+ NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
+ NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
+ NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
+ public @interface NasProtocolMessage {
+ }
+
+ public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0;
+ public static final int CELLULAR_IDENTIFIER_IMSI = 1;
+ public static final int CELLULAR_IDENTIFIER_IMEI = 2;
+ public static final int CELLULAR_IDENTIFIER_SUCI = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CELLULAR_IDENTIFIER_"}, value = {CELLULAR_IDENTIFIER_UNKNOWN,
+ CELLULAR_IDENTIFIER_IMSI, CELLULAR_IDENTIFIER_IMEI, CELLULAR_IDENTIFIER_SUCI})
+ public @interface CellularIdentifier {
+ }
+}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 4548a7df6874..1e5f33f26dc2 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -710,9 +710,15 @@ public class GraphicsActivity extends Activity {
float childFrameRate = Collections.max(frameRates);
float parentFrameRate = childFrameRate / 2;
int initialNumEvents = mModeChangedEvents.size();
- parent.setFrameRate(parentFrameRate);
parent.setFrameRateSelectionStrategy(parentStrategy);
- child.setFrameRate(childFrameRate);
+
+ // For Self case, we want to test that child gets default behavior
+ if (parentStrategy == SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF) {
+ parent.setFrameRateCategory(Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ } else {
+ parent.setFrameRate(parentFrameRate);
+ child.setFrameRate(childFrameRate);
+ }
// Verify
float expectedFrameRate =
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index bed9cff75e1d..29f6879f37c3 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -16,11 +16,8 @@
package android.view.surfacecontroltests;
-import static org.junit.Assume.assumeTrue;
-
import android.Manifest;
import android.hardware.display.DisplayManager;
-import android.os.SystemProperties;
import android.support.test.uiautomator.UiDevice;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -54,10 +51,6 @@ public class SurfaceControlTest {
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- // TODO(b/290634611): clean this up once SF new front end is enabled by default
- assumeTrue(SystemProperties.getBoolean(
- "persist.debug.sf.enable_layer_lifecycle_manager", false));
-
uiDevice.wakeUp();
uiDevice.executeShellCommand("wm dismiss-keyguard");
@@ -118,10 +111,11 @@ public class SurfaceControlTest {
}
@Test
- public void testSurfaceControlFrameRateSelectionStrategySelf() throws InterruptedException {
+ public void testSurfaceControlFrameRateSelectionStrategyPropagate()
+ throws InterruptedException {
GraphicsActivity activity = mActivityRule.getActivity();
activity.testSurfaceControlFrameRateSelectionStrategy(
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
}
@Test
@@ -131,4 +125,12 @@ public class SurfaceControlTest {
activity.testSurfaceControlFrameRateSelectionStrategy(
SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
+
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategySelf()
+ throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ }
}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index d075b5f01ef9..5434c82b07bd 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -51,6 +51,7 @@ public class InputDeviceTest {
assertEquals(device.getName(), outDevice.getName());
assertEquals(device.getVendorId(), outDevice.getVendorId());
assertEquals(device.getProductId(), outDevice.getProductId());
+ assertEquals(device.getDeviceBus(), outDevice.getDeviceBus());
assertEquals(device.getDescriptor(), outDevice.getDescriptor());
assertEquals(device.isExternal(), outDevice.isExternal());
assertEquals(device.getSources(), outDevice.getSources());
@@ -79,6 +80,7 @@ public class InputDeviceTest {
.setName("Test Device " + DEVICE_ID)
.setVendorId(44)
.setProductId(45)
+ .setDeviceBus(3)
.setDescriptor("descriptor")
.setExternal(true)
.setSources(InputDevice.SOURCE_HDMI)
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 85038be80c51..91e6814ed243 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -51,6 +51,8 @@ ANNOTATION_FILTER=$TEMP/annotation-filter.txt
HOSTSTUBGEN_OUT=$TEMP/output.txt
+EXTRA_ARGS=""
+
# Because of `set -e`, we can't return non-zero from functions, so we store
# HostStubGen result in it.
HOSTSTUBGEN_RC=0
@@ -115,6 +117,7 @@ run_hoststubgen() {
--keep-static-initializer-annotation \
android.hosttest.annotation.HostSideTestStaticInitializerKeep \
$filter_arg \
+ $EXTRA_ARGS \
|& tee $HOSTSTUBGEN_OUT
HOSTSTUBGEN_RC=${PIPESTATUS[0]}
echo "HostStubGen exited with $HOSTSTUBGEN_RC"
@@ -209,7 +212,6 @@ com.supported.*
com.unsupported.*
"
-
run_hoststubgen_for_failure "One specific class disallowed" \
"TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \
"
@@ -229,6 +231,14 @@ IMPL="" run_hoststubgen_for_success "No impl generation" ""
STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" ""
+EXTRA_ARGS="--in-jar abc" run_hoststubgen_for_failure "Duplicate arg" \
+ "Duplicate or conflicting argument found: --in-jar" \
+ ""
+
+EXTRA_ARGS="--quiet" run_hoststubgen_for_failure "Conflicting arg" \
+ "Duplicate or conflicting argument found: --quiet" \
+ ""
+
echo "All tests passed"
exit 0 \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 3cdddc23b332..dbcf3a5207e5 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -22,7 +22,6 @@ import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.ImplicitOutputFilter
-import com.android.hoststubgen.filters.KeepAllClassesFilter
import com.android.hoststubgen.filters.OutputFilter
import com.android.hoststubgen.filters.StubIntersectingFilter
import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
@@ -52,15 +51,15 @@ class HostStubGen(val options: HostStubGenOptions) {
val errors = HostStubGenErrors()
// Load all classes.
- val allClasses = loadClassStructures(options.inJar)
+ val allClasses = loadClassStructures(options.inJar.get)
// Dump the classes, if specified.
- options.inputJarDumpFile?.let {
+ options.inputJarDumpFile.ifSet {
PrintWriter(it).use { pw -> allClasses.dump(pw) }
log.i("Dump file created at $it")
}
- options.inputJarAsKeepAllFile?.let {
+ options.inputJarAsKeepAllFile.ifSet {
PrintWriter(it).use {
pw -> allClasses.forEach {
classNode -> printAsTextPolicy(pw, classNode)
@@ -74,11 +73,11 @@ class HostStubGen(val options: HostStubGenOptions) {
// Transform the jar.
convert(
- options.inJar,
- options.outStubJar,
- options.outImplJar,
+ options.inJar.get,
+ options.outStubJar.get,
+ options.outImplJar.get,
filter,
- options.enableClassChecker,
+ options.enableClassChecker.get,
allClasses,
errors,
)
@@ -153,7 +152,7 @@ class HostStubGen(val options: HostStubGenOptions) {
// text-file based filter, which is handled by parseTextFilterPolicyFile.
// The first filter is for the default policy from the command line options.
- var filter: OutputFilter = ConstantFilter(options.defaultPolicy, "default-by-options")
+ var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
// Next, we need a filter that resolves "class-wide" policies.
// This is used when a member (methods, fields, nested classes) don't get any polices
@@ -163,16 +162,16 @@ class HostStubGen(val options: HostStubGenOptions) {
// Inject default hooks from options.
filter = DefaultHookInjectingFilter(
- options.defaultClassLoadHook,
- options.defaultMethodCallHook,
+ options.defaultClassLoadHook.get,
+ options.defaultMethodCallHook.get,
filter
)
- val annotationAllowedClassesFilter = options.annotationAllowedClassesFile.let { filename ->
- if (filename == null) {
+ val annotationAllowedClassesFilter = options.annotationAllowedClassesFile.get.let { file ->
+ if (file == null) {
ClassFilter.newNullFilter(true) // Allow all classes
} else {
- ClassFilter.loadFromFile(filename, false)
+ ClassFilter.loadFromFile(file, false)
}
}
@@ -196,7 +195,7 @@ class HostStubGen(val options: HostStubGenOptions) {
// Next, "text based" filter, which allows to override polices without touching
// the target code.
- options.policyOverrideFile?.let {
+ options.policyOverrideFile.ifSet {
filter = createFilterFromTextPolicyFile(it, allClasses, filter)
}
@@ -212,11 +211,6 @@ class HostStubGen(val options: HostStubGenOptions) {
// Apply the implicit filter.
filter = ImplicitOutputFilter(errors, allClasses, filter)
- // Optionally keep all classes.
- if (options.keepAllClasses) {
- filter = KeepAllClassesFilter(filter)
- }
-
return filter
}
@@ -422,9 +416,9 @@ class HostStubGen(val options: HostStubGenOptions) {
outVisitor = CheckClassAdapter(outVisitor)
}
val visitorOptions = BaseAdapter.Options(
- enablePreTrace = options.enablePreTrace,
- enablePostTrace = options.enablePostTrace,
- enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection,
+ enablePreTrace = options.enablePreTrace.get,
+ enablePostTrace = options.enablePostTrace.get,
+ enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection.get,
errors = errors,
)
outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 83f873d38f1b..0ae52afb73e4 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -21,21 +21,60 @@ import java.io.File
import java.io.FileReader
/**
+ * A single value that can only set once.
+ */
+class SetOnce<T>(
+ private var value: T,
+) {
+ class SetMoreThanOnceException : Exception()
+
+ private var set = false
+
+ fun set(v: T) {
+ if (set) {
+ throw SetMoreThanOnceException()
+ }
+ if (v == null) {
+ throw NullPointerException("This shouldn't happen")
+ }
+ set = true
+ value = v
+ }
+
+ val get: T
+ get() = this.value
+
+ val isSet: Boolean
+ get() = this.set
+
+ fun <R> ifSet(block: (T & Any) -> R): R? {
+ if (isSet) {
+ return block(value!!)
+ }
+ return null
+ }
+
+ override fun toString(): String {
+ return "$value"
+ }
+}
+
+/**
* Options that can be set from command line arguments.
*/
class HostStubGenOptions(
/** Input jar file*/
- var inJar: String = "",
+ var inJar: SetOnce<String> = SetOnce(""),
/** Output stub jar file */
- var outStubJar: String? = null,
+ var outStubJar: SetOnce<String?> = SetOnce(null),
/** Output implementation jar file */
- var outImplJar: String? = null,
+ var outImplJar: SetOnce<String?> = SetOnce(null),
- var inputJarDumpFile: String? = null,
+ var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
- var inputJarAsKeepAllFile: String? = null,
+ var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
var stubAnnotations: MutableSet<String> = mutableSetOf(),
var keepAnnotations: MutableSet<String> = mutableSetOf(),
@@ -51,27 +90,26 @@ class HostStubGenOptions(
var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
- var annotationAllowedClassesFile: String? = null,
+ var annotationAllowedClassesFile: SetOnce<String?> = SetOnce(null),
- var defaultClassLoadHook: String? = null,
- var defaultMethodCallHook: String? = null,
+ var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
+ var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
var intersectStubJars: MutableSet<String> = mutableSetOf(),
- var policyOverrideFile: String? = null,
+ var policyOverrideFile: SetOnce<String?> = SetOnce(null),
- var defaultPolicy: FilterPolicy = FilterPolicy.Remove,
- var keepAllClasses: Boolean = false,
+ var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
- var logLevel: LogLevel = LogLevel.Info,
+ var logLevel: SetOnce<LogLevel> = SetOnce(LogLevel.Info),
- var cleanUpOnError: Boolean = false,
+ var cleanUpOnError: SetOnce<Boolean> = SetOnce(true),
- var enableClassChecker: Boolean = false,
- var enablePreTrace: Boolean = false,
- var enablePostTrace: Boolean = false,
+ var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
+ var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
+ var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
- var enableNonStubMethodCallDetection: Boolean = false,
+ var enableNonStubMethodCallDetection: SetOnce<Boolean> = SetOnce(false),
) {
companion object {
@@ -111,110 +149,120 @@ class HostStubGenOptions(
break
}
- when (arg) {
- // TODO: Write help
- "-h", "--h" -> TODO("Help is not implemented yet")
+ // Define some shorthands...
+ fun nextArg(): String = ai.nextArgRequired(arg)
+ fun SetOnce<String>.setNextStringArg(): String = nextArg().also { this.set(it) }
+ fun SetOnce<String?>.setNextStringArg(): String = nextArg().also { this.set(it) }
+ fun MutableSet<String>.addUniqueAnnotationArg(): String =
+ nextArg().also { this += ensureUniqueAnnotation(it) }
+
+ try {
+ when (arg) {
+ // TODO: Write help
+ "-h", "--help" -> TODO("Help is not implemented yet")
- "-v", "--verbose" -> ret.logLevel = LogLevel.Verbose
- "-d", "--debug" -> ret.logLevel = LogLevel.Debug
- "-q", "--quiet" -> ret.logLevel = LogLevel.None
+ "-v", "--verbose" -> ret.logLevel.set(LogLevel.Verbose)
+ "-d", "--debug" -> ret.logLevel.set(LogLevel.Debug)
+ "-q", "--quiet" -> ret.logLevel.set(LogLevel.None)
- "--in-jar" -> ret.inJar = ai.nextArgRequired(arg).ensureFileExists()
- "--out-stub-jar" -> ret.outStubJar = ai.nextArgRequired(arg)
- "--out-impl-jar" -> ret.outImplJar = ai.nextArgRequired(arg)
+ "--in-jar" -> ret.inJar.setNextStringArg().ensureFileExists()
+ "--out-stub-jar" -> ret.outStubJar.setNextStringArg()
+ "--out-impl-jar" -> ret.outImplJar.setNextStringArg()
- "--policy-override-file" ->
- ret.policyOverrideFile = ai.nextArgRequired(arg).ensureFileExists()
+ "--policy-override-file" ->
+ ret.policyOverrideFile.setNextStringArg().ensureFileExists()
- "--clean-up-on-error" -> ret.cleanUpOnError = true
- "--no-clean-up-on-error" -> ret.cleanUpOnError = false
+ "--clean-up-on-error" -> ret.cleanUpOnError.set(true)
+ "--no-clean-up-on-error" -> ret.cleanUpOnError.set(false)
- "--default-remove" -> ret.defaultPolicy = FilterPolicy.Remove
- "--default-throw" -> ret.defaultPolicy = FilterPolicy.Throw
- "--default-keep" -> ret.defaultPolicy = FilterPolicy.Keep
- "--default-stub" -> ret.defaultPolicy = FilterPolicy.Stub
+ "--default-remove" -> ret.defaultPolicy.set(FilterPolicy.Remove)
+ "--default-throw" -> ret.defaultPolicy.set(FilterPolicy.Throw)
+ "--default-keep" -> ret.defaultPolicy.set(FilterPolicy.Keep)
+ "--default-stub" -> ret.defaultPolicy.set(FilterPolicy.Stub)
- "--keep-all-classes" -> ret.keepAllClasses = true
- "--no-keep-all-classes" -> ret.keepAllClasses = false
+ "--stub-annotation" ->
+ ret.stubAnnotations.addUniqueAnnotationArg()
- "--stub-annotation" ->
- ret.stubAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-annotation" ->
+ ret.keepAnnotations.addUniqueAnnotationArg()
- "--keep-annotation" ->
- ret.keepAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--stub-class-annotation" ->
+ ret.stubClassAnnotations.addUniqueAnnotationArg()
- "--stub-class-annotation" ->
- ret.stubClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-class-annotation" ->
+ ret.keepClassAnnotations.addUniqueAnnotationArg()
- "--keep-class-annotation" ->
- ret.keepClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--throw-annotation" ->
+ ret.throwAnnotations.addUniqueAnnotationArg()
- "--throw-annotation" ->
- ret.throwAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--remove-annotation" ->
+ ret.removeAnnotations.addUniqueAnnotationArg()
- "--remove-annotation" ->
- ret.removeAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--substitute-annotation" ->
+ ret.substituteAnnotations.addUniqueAnnotationArg()
- "--substitute-annotation" ->
- ret.substituteAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--native-substitute-annotation" ->
+ ret.nativeSubstituteAnnotations.addUniqueAnnotationArg()
- "--native-substitute-annotation" ->
- ret.nativeSubstituteAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--class-load-hook-annotation" ->
+ ret.classLoadHookAnnotations.addUniqueAnnotationArg()
- "--class-load-hook-annotation" ->
- ret.classLoadHookAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-static-initializer-annotation" ->
+ ret.keepStaticInitializerAnnotations.addUniqueAnnotationArg()
- "--keep-static-initializer-annotation" ->
- ret.keepStaticInitializerAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--package-redirect" ->
+ ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))
- "--package-redirect" ->
- ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))
+ "--annotation-allowed-classes-file" ->
+ ret.annotationAllowedClassesFile.setNextStringArg()
- "--annotation-allowed-classes-file" ->
- ret.annotationAllowedClassesFile = ai.nextArgRequired(arg)
+ "--default-class-load-hook" ->
+ ret.defaultClassLoadHook.setNextStringArg()
- "--default-class-load-hook" ->
- ret.defaultClassLoadHook = ai.nextArgRequired(arg)
+ "--default-method-call-hook" ->
+ ret.defaultMethodCallHook.setNextStringArg()
- "--default-method-call-hook" ->
- ret.defaultMethodCallHook = ai.nextArgRequired(arg)
+ "--intersect-stub-jar" ->
+ ret.intersectStubJars += nextArg().ensureFileExists()
- "--intersect-stub-jar" ->
- ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()
+ "--gen-keep-all-file" ->
+ ret.inputJarAsKeepAllFile.setNextStringArg()
- "--gen-keep-all-file" ->
- ret.inputJarAsKeepAllFile = ai.nextArgRequired(arg)
+ // Following options are for debugging.
+ "--enable-class-checker" -> ret.enableClassChecker.set(true)
+ "--no-class-checker" -> ret.enableClassChecker.set(false)
- // Following options are for debugging.
- "--enable-class-checker" -> ret.enableClassChecker = true
- "--no-class-checker" -> ret.enableClassChecker = false
+ "--enable-pre-trace" -> ret.enablePreTrace.set(true)
+ "--no-pre-trace" -> ret.enablePreTrace.set(false)
- "--enable-pre-trace" -> ret.enablePreTrace = true
- "--no-pre-trace" -> ret.enablePreTrace = false
+ "--enable-post-trace" -> ret.enablePostTrace.set(true)
+ "--no-post-trace" -> ret.enablePostTrace.set(false)
- "--enable-post-trace" -> ret.enablePostTrace = true
- "--no-post-trace" -> ret.enablePostTrace = false
+ "--enable-non-stub-method-check" ->
+ ret.enableNonStubMethodCallDetection.set(true)
- "--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true
- "--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false
+ "--no-non-stub-method-check" ->
+ ret.enableNonStubMethodCallDetection.set(false)
- "--gen-input-dump-file" -> ret.inputJarDumpFile = ai.nextArgRequired(arg)
+ "--gen-input-dump-file" -> ret.inputJarDumpFile.setNextStringArg()
- else -> throw ArgumentsException("Unknown option: $arg")
+ else -> throw ArgumentsException("Unknown option: $arg")
+ }
+ } catch (e: SetOnce.SetMoreThanOnceException) {
+ throw ArgumentsException("Duplicate or conflicting argument found: $arg")
}
}
- if (ret.inJar.isEmpty()) {
+ log.w(ret.toString())
+
+ if (!ret.inJar.isSet) {
throw ArgumentsException("Required option missing: --in-jar")
}
- if (ret.outStubJar == null && ret.outImplJar == null) {
+ if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
" $COMMAND_NAME will not generate jar files.")
}
- if (ret.enableNonStubMethodCallDetection) {
+ if (ret.enableNonStubMethodCallDetection.get) {
log.w("--enable-non-stub-method-check is not fully implemented yet." +
" See the todo in doesMethodNeedNonStubCallCheck().")
}
@@ -329,7 +377,6 @@ class HostStubGenOptions(
intersectStubJars=$intersectStubJars,
policyOverrideFile=$policyOverrideFile,
defaultPolicy=$defaultPolicy,
- keepAllClasses=$keepAllClasses,
logLevel=$logLevel,
cleanUpOnError=$cleanUpOnError,
enableClassChecker=$enableClassChecker,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
index 0321d9db03ed..38ba0ccfd14f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
@@ -28,9 +28,9 @@ fun main(args: Array<String>) {
try {
// Parse the command line arguments.
val options = HostStubGenOptions.parseArgs(args)
- clanupOnError = options.cleanUpOnError
+ clanupOnError = options.cleanUpOnError.get
- log.level = options.logLevel
+ log.level = options.logLevel.get
log.v("HostStubGen started")
log.v("Options: $options")
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 356e1fa1327c..8354d985abfd 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -39,5 +39,5 @@ class AndroidHeuristicsFilter(
private fun ClassNodes.isAidlClass(className: String): Boolean {
return hasClass(className) &&
hasClass("$className\$Stub") &&
- hasClass("$className\$Proxy")
+ hasClass("$className\$Stub\$Proxy")
} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 214de59bbb4d..3956893ee7ed 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -223,16 +223,16 @@ RuntimeVisibleAnnotations:
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -243,7 +243,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -262,7 +262,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
SourceFile: "IPretendingAidl.java"
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
Compiled from "IPretendingAidl.java"
public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
@@ -303,6 +304,7 @@ SourceFile: "IPretendingAidl.java"
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
Compiled from "IPretendingAidl.java"
public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
@@ -315,11 +317,11 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
}
SourceFile: "IPretendingAidl.java"
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 90312289db1c..9349355d4d02 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@ RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index e01f49baf320..4f8c408eb7d9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -205,16 +205,16 @@ RuntimeVisibleAnnotations:
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -225,7 +225,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -242,7 +242,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
0 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -288,6 +289,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -306,8 +308,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -315,8 +317,8 @@ RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 90312289db1c..9349355d4d02 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@ RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 5246355fb777..5ff3cded38b3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -289,13 +289,13 @@ RuntimeVisibleAnnotations:
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 4
private static {};
@@ -303,17 +303,17 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String <init>
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -324,14 +324,14 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String addTwo
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -346,7 +346,8 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
11 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -412,6 +413,7 @@ public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -439,8 +441,8 @@ public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
x: return
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -448,8 +450,8 @@ RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
index 583e13c5573b..0a07c2b91fc3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
@@ -25,11 +25,12 @@ public interface IPretendingAidl {
public static int addOne(int a) {
return a + 1;
}
- }
- public static class Proxy {
- public static int addTwo(int a) {
- return a + 2;
+ public static class Proxy {
+ public static int addTwo(int a) {
+ return a + 2;
+ }
}
}
+
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 0d527915ef63..d3501057163d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -289,6 +289,6 @@ public class TinyFrameworkClassTest {
@Test
public void testAidlHeuristics() {
assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
- assertThat(IPretendingAidl.Proxy.addTwo(1)).isEqualTo(3);
+ assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
}
}