summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Xin Li <delphij@google.com> 2024-08-08 11:39:46 -0700
committer Xin Li <delphij@google.com> 2024-08-08 11:39:46 -0700
commite7e6ec868a3277517795e77976186fc0dfed9365 (patch)
tree30e55d3bbab99ac3d30c9ea8b97bce9b67c59f1b
parent2008d30e49a986a4cfc863b46b7ec668c7735603 (diff)
parenta3ec1eab222414913d2696bf076e899bf6bf93ff (diff)
Merge 24Q3 (ab/AP3A.240905.001) to aosp-main-future
Bug: 347831320 Merged-In: Ib851a4cb2332902f4c96eec5131eb7d5aff99dd5 Change-Id: Ieba5f32b7f917c795a9e18cc53b86da0bb5fa2fa
-rw-r--r--core/java/android/app/AppOpsManager.java4
-rw-r--r--core/java/android/app/ResourcesManager.java262
-rw-r--r--core/java/android/content/res/AssetManager.java124
-rw-r--r--core/java/android/content/res/ResourcesImpl.java26
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java12
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java2
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java11
-rw-r--r--core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java5
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java2
-rw-r--r--core/java/android/hardware/camera2/params/SessionConfiguration.java4
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java19
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java8
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl3
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/chooser/flags.aconfig11
-rw-r--r--core/java/android/view/InsetsAnimationThreadControlRunner.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java13
-rw-r--r--core/java/android/window/TransitionInfo.java2
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig13
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig11
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java16
-rw-r--r--core/res/res/values/config_telephony.xml6
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java16
-rw-r--r--data/etc/com.android.systemui.xml1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java11
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java45
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java28
-rw-r--r--media/java/android/media/AudioManager.java17
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaRoute2Info.java29
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java22
-rw-r--r--packages/PackageInstaller/res/layout/install_content_view.xml20
-rw-r--r--packages/PackageInstaller/res/layout/uninstall_content_view.xml50
-rw-r--r--packages/PackageInstaller/res/values-night/themes.xml1
-rw-r--r--packages/PackageInstaller/res/values/themes.xml1
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt28
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt2
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt4
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt13
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java20
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig11
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt9
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt9
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt74
-rw-r--r--packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml2
-rw-r--r--packages/SystemUI/res/color/connected_network_primary_color.xml20
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml3
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_on.xml5
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_on_background.xml3
-rw-r--r--packages/SystemUI/res/layout/screenshot_shelf.xml2
-rw-r--r--packages/SystemUI/res/layout/sidefps_view.xml3
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml1
-rw-r--r--packages/SystemUI/res/values-night/colors.xml4
-rw-r--r--packages/SystemUI/res/values-night/styles.xml12
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt116
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt359
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt122
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt144
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt10
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/RequestId.java152
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java22
-rw-r--r--services/backup/java/com/android/server/backup/utils/TarBackupReader.java3
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java8
-rw-r--r--services/core/java/com/android/server/SystemConfig.java2
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java9
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java63
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java27
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java13
-rw-r--r--services/core/java/com/android/server/display/BrightnessSetting.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java16
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java4
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java19
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java7
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java2
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java9
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java7
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java215
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java32
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java25
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java5
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java99
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java9
-rw-r--r--services/core/java/com/android/server/power/stats/GnssPowerCalculator.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java84
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java5
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java14
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/Session.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java9
-rw-r--r--services/core/java/com/android/server/wm/Transition.java33
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java14
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java15
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt9
-rw-r--r--services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java86
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java161
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java58
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java22
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java22
201 files changed, 3175 insertions, 1363 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2313fa27afe1..5214d2c9c02a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2981,9 +2981,7 @@ public class AppOpsManager {
new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER,
"ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS,
- "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(
- android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
- ? MODE_DEFAULT : MODE_ALLOWED)
+ "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
.setDisableReset(true).setRestrictRead(true).build(),
new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
"RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 370aff8e7a0f..b51462e6b363 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -30,6 +30,7 @@ import android.content.res.AssetManager;
import android.content.res.CompatResources;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Flags;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
@@ -138,16 +139,22 @@ public class ResourcesManager {
private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap =
new ArrayMap<>();
+ @VisibleForTesting
+ public ArrayMap<String, SharedLibraryAssets> getRegisteredResourcePaths() {
+ return mSharedLibAssetsMap;
+ }
+
/**
* The internal function to register the resources paths of a package (e.g. a shared library).
* This will collect the package resources' paths from its ApplicationInfo and add them to all
* existing and future contexts while the application is running.
*/
public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) {
- SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir,
- appInfo.splitSourceDirs, appInfo.sharedLibraryFiles,
- appInfo.resourceDirs, appInfo.overlayPaths);
+ if (!Flags.registerResourcePaths()) {
+ return;
+ }
+ final var sharedLibAssets = new SharedLibraryAssets(appInfo);
synchronized (mLock) {
if (mSharedLibAssetsMap.containsKey(uniqueId)) {
Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId
@@ -155,18 +162,37 @@ public class ResourcesManager {
return;
}
mSharedLibAssetsMap.put(uniqueId, sharedLibAssets);
- appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths());
- Slog.v(TAG, "The following resources' paths have been added: "
- + Arrays.toString(sharedLibAssets.getAllAssetPaths()));
+ appendLibAssetsLocked(sharedLibAssets);
+ Slog.v(TAG, "The following library key has been added: "
+ + sharedLibAssets.getResourcesKey());
}
}
- private static class ApkKey {
+ /**
+ * Apply the registered library paths to the passed impl object
+ * @return the hash code for the current version of the registered paths
+ */
+ public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+ if (!Flags.registerResourcePaths()) {
+ return 0;
+ }
+
+ final var collector = new PathCollector(null);
+ final int size = mSharedLibAssetsMap.size();
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
+ }
+ impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return size;
+ }
+
+ public static class ApkKey {
public final String path;
public final boolean sharedLib;
public final boolean overlay;
- ApkKey(String path, boolean sharedLib, boolean overlay) {
+ public ApkKey(String path, boolean sharedLib, boolean overlay) {
this.path = path;
this.sharedLib = sharedLib;
this.overlay = overlay;
@@ -190,6 +216,12 @@ public class ResourcesManager {
return this.path.equals(other.path) && this.sharedLib == other.sharedLib
&& this.overlay == other.overlay;
}
+
+ @Override
+ public String toString() {
+ return "ApkKey[" + (sharedLib ? "lib" : "app") + (overlay ? ", overlay" : "") + ": "
+ + path + "]";
+ }
}
/**
@@ -505,7 +537,10 @@ public class ResourcesManager {
return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
}
- private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
+ /**
+ * Loads the ApkAssets object for the passed key, or picks the one from the cache if available.
+ */
+ public @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
ApkAssets apkAssets;
// Optimistically check if this ApkAssets exists somewhere else.
@@ -747,8 +782,8 @@ public class ResourcesManager {
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
- // ResourcesImpl also need to be recreated if its shared library count is not up-to-date.
- if (impl == null || impl.getSharedLibCount() != mSharedLibAssetsMap.size()) {
+ // ResourcesImpl also need to be recreated if its shared library hash is not up-to-date.
+ if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) {
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
mResourceImpls.put(key, new WeakReference<>(impl));
@@ -1533,56 +1568,109 @@ public class ResourcesManager {
}
}
- private void appendLibAssetsLocked(String[] libAssets) {
- synchronized (mLock) {
- // Record which ResourcesImpl need updating
- // (and what ResourcesKey they should update to).
- final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ /**
+ * A utility class to collect resources paths into a ResourcesKey object:
+ * - Separates the libraries and the overlays into different sets as those are loaded in
+ * different ways.
+ * - Allows to start with an existing original key object, and copies all non-path related
+ * properties into the final one.
+ * - Preserves the path order while dropping all duplicates in an efficient manner.
+ */
+ private static class PathCollector {
+ public final ResourcesKey originalKey;
+ public final ArrayList<String> orderedLibs = new ArrayList<>();
+ public final ArraySet<String> libsSet = new ArraySet<>();
+ public final ArrayList<String> orderedOverlays = new ArrayList<>();
+ public final ArraySet<String> overlaysSet = new ArraySet<>();
+
+ static void appendNewPath(@NonNull String path,
+ @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) {
+ if (uniquePaths.add(path)) {
+ orderedPaths.add(path);
+ }
+ }
- final int implCount = mResourceImpls.size();
- for (int i = 0; i < implCount; i++) {
- final ResourcesKey key = mResourceImpls.keyAt(i);
- final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl == null) {
- Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to "
- + "append shared library assets for next ResourcesImpl.");
- continue;
- }
+ static void appendAllNewPaths(@Nullable String[] paths,
+ @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) {
+ if (paths == null) return;
+ for (int i = 0, size = paths.length; i < size; i++) {
+ appendNewPath(paths[i], uniquePaths, orderedPaths);
+ }
+ }
- var newDirs = new ArrayList<String>();
- var dirsSet = new ArraySet<String>();
- if (key.mLibDirs != null) {
- final int dirsLength = key.mLibDirs.length;
- for (int k = 0; k < dirsLength; k++) {
- newDirs.add(key.mLibDirs[k]);
- dirsSet.add(key.mLibDirs[k]);
- }
- }
- final int assetsLength = libAssets.length;
- for (int j = 0; j < assetsLength; j++) {
- if (dirsSet.add(libAssets[j])) {
- newDirs.add(libAssets[j]);
- }
- }
- String[] newLibAssets = newDirs.toArray(new String[0]);
- if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
- updatedResourceKeys.put(impl, new ResourcesKey(
- key.mResDir,
- key.mSplitResDirs,
- key.mOverlayPaths,
- newLibAssets,
- key.mDisplayId,
- key.mOverrideConfiguration,
- key.mCompatInfo,
- key.mLoaders));
- }
+ PathCollector(@Nullable ResourcesKey original) {
+ originalKey = original;
+ if (originalKey != null) {
+ appendKey(originalKey);
}
+ }
- redirectAllResourcesToNewImplLocked(updatedResourceKeys);
+ public void appendKey(@NonNull ResourcesKey key) {
+ appendAllNewPaths(key.mLibDirs, libsSet, orderedLibs);
+ appendAllNewPaths(key.mOverlayPaths, overlaysSet, orderedOverlays);
+ }
+
+ boolean isSameAsOriginal() {
+ if (originalKey == null) {
+ return orderedLibs.isEmpty() && orderedOverlays.isEmpty();
+ }
+ return ((originalKey.mLibDirs == null && orderedLibs.isEmpty())
+ || (originalKey.mLibDirs != null
+ && originalKey.mLibDirs.length == orderedLibs.size()))
+ && ((originalKey.mOverlayPaths == null && orderedOverlays.isEmpty())
+ || (originalKey.mOverlayPaths != null
+ && originalKey.mOverlayPaths.length == orderedOverlays.size()));
+ }
+
+ @NonNull ResourcesKey collectedKey() {
+ return new ResourcesKey(
+ originalKey == null ? null : originalKey.mResDir,
+ originalKey == null ? null : originalKey.mSplitResDirs,
+ orderedOverlays.toArray(new String[0]), orderedLibs.toArray(new String[0]),
+ originalKey == null ? 0 : originalKey.mDisplayId,
+ originalKey == null ? null : originalKey.mOverrideConfiguration,
+ originalKey == null ? null : originalKey.mCompatInfo,
+ originalKey == null ? null : originalKey.mLoaders);
}
}
+ /**
+ * Takes the original resources key and the one containing a set of library paths and overlays
+ * to append, and combines them together. In case when the original key already contains all
+ * those paths this function returns null, otherwise it makes a new ResourcesKey object.
+ */
+ private @Nullable ResourcesKey createNewResourceKeyIfNeeded(
+ @NonNull ResourcesKey original, @NonNull ResourcesKey library) {
+ final var collector = new PathCollector(original);
+ collector.appendKey(library);
+ return collector.isSameAsOriginal() ? null : collector.collectedKey();
+ }
+
+ /**
+ * Append the newly registered shared library asset paths to all existing resources objects.
+ */
+ private void appendLibAssetsLocked(@NonNull SharedLibraryAssets libAssets) {
+ // Record the ResourcesImpl's that need updating, and what ResourcesKey they should
+ // update to.
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl == null) {
+ Slog.w(TAG, "Found a null ResourcesImpl, skipped.");
+ continue;
+ }
+
+ final var newKey = createNewResourceKeyIfNeeded(key, libAssets.getResourcesKey());
+ if (newKey != null) {
+ updatedResourceKeys.put(impl, newKey);
+ }
+ }
+ redirectAllResourcesToNewImplLocked(updatedResourceKeys);
+ }
+
private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs,
@NonNull final ApplicationInfo appInfo) {
try {
@@ -1718,8 +1806,9 @@ public class ResourcesManager {
}
}
- // Another redirect function which will loop through all Resources and reload ResourcesImpl
- // if it needs a shared library asset paths update.
+ // Another redirect function which will loop through all Resources in the process, even the ones
+ // the app created outside of the regular Android Runtime, and reload their ResourcesImpl if it
+ // needs a shared library asset paths update.
private void redirectAllResourcesToNewImplLocked(
@NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue);
@@ -1830,52 +1919,35 @@ public class ResourcesManager {
}
}
- public static class SharedLibraryAssets{
- private final String[] mAssetPaths;
-
- SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles,
- String[] resourceDirs, String[] overlayPaths) {
- mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles,
- resourceDirs, overlayPaths);
- }
-
- private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs,
- String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) {
- final String[][] inputLists = {
- splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
- };
-
- final ArraySet<String> assetPathSet = new ArraySet<>();
- final List<String> assetPathList = new ArrayList<>();
- if (sourceDir != null) {
- assetPathSet.add(sourceDir);
- assetPathList.add(sourceDir);
- }
-
- for (int i = 0; i < inputLists.length; i++) {
- if (inputLists[i] != null) {
- for (int j = 0; j < inputLists[i].length; j++) {
- if (assetPathSet.add(inputLists[i][j])) {
- assetPathList.add(inputLists[i][j]);
- }
- }
- }
- }
- return assetPathList.toArray(new String[0]);
+ @VisibleForTesting
+ public static class SharedLibraryAssets {
+ private final ResourcesKey mResourcesKey;
+
+ private SharedLibraryAssets(ApplicationInfo appInfo) {
+ // We're loading all library's files as shared libs, regardless where they are in
+ // its own ApplicationInfo.
+ final var collector = new PathCollector(null);
+ PathCollector.appendNewPath(appInfo.sourceDir, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.splitSourceDirs, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.sharedLibraryFiles, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.resourceDirs, collector.overlaysSet,
+ collector.orderedOverlays);
+ PathCollector.appendAllNewPaths(appInfo.overlayPaths, collector.overlaysSet,
+ collector.orderedOverlays);
+ mResourcesKey = collector.collectedKey();
}
/**
- * @return all the asset paths of this collected in this class.
+ * @return the resources key for this library assets.
*/
- public @NonNull String[] getAllAssetPaths() {
- return mAssetPaths;
+ public @NonNull ResourcesKey getResourcesKey() {
+ return mResourcesKey;
}
}
- public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() {
- return new ArrayMap<>(mSharedLibAssetsMap);
- }
-
/**
* Add all resources references to the list which is designed to help to append shared library
* asset paths. This is invoked in Resources constructor to include all Resources instances.
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 273e40a21bb2..fd9361d4f2d8 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -17,6 +17,7 @@
package android.content.res;
import static android.content.res.Resources.ID_NULL;
+import static android.app.ResourcesManager.ApkKey;
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
@@ -26,12 +27,14 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.TestApi;
+import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
import android.content.res.loader.ResourcesLoader;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -264,7 +267,7 @@ public final class AssetManager implements AutoCloseable {
}
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
- sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+ sSystemApkAssets = apkAssets.toArray(new ApkAssets[0]);
if (sSystem == null) {
sSystem = new AssetManager(true /*sentinel*/);
}
@@ -448,7 +451,7 @@ public final class AssetManager implements AutoCloseable {
@Deprecated
@UnsupportedAppUsage
public int addAssetPath(String path) {
- return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, false, false)), false);
}
/**
@@ -458,7 +461,7 @@ public final class AssetManager implements AutoCloseable {
@Deprecated
@UnsupportedAppUsage
public int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, true, false)), false);
}
/**
@@ -468,55 +471,110 @@ public final class AssetManager implements AutoCloseable {
@Deprecated
@UnsupportedAppUsage
public int addOverlayPath(String path) {
- return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, false, true)), false);
}
/**
* @hide
*/
- public void addSharedLibraryPaths(@NonNull String[] paths) {
- final int length = paths.length;
- for (int i = 0; i < length; i++) {
- addAssetPathInternal(paths[i], false, true);
- }
+ public void addPresetApkKeys(@NonNull List<ApkKey> keys) {
+ addAssetPathInternal(keys, true);
}
- private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
- Objects.requireNonNull(path, "path");
+ private int addAssetPathInternal(List<ApkKey> apkKeys, boolean presetAssets) {
+ Objects.requireNonNull(apkKeys, "apkKeys");
+ if (apkKeys.isEmpty()) {
+ return 0;
+ }
+
synchronized (this) {
ensureOpenLocked();
final int count = mApkAssets.length;
- // See if we already have it loaded.
- for (int i = 0; i < count; i++) {
- if (mApkAssets[i].getAssetPath().equals(path)) {
- return i + 1;
- }
+ // See if we already have some of the apkKeys loaded.
+ final int originalAssetsCount = mApkAssets.length;
+
+ // Getting an assets' path is a relatively expensive operation, cache them.
+ final ArrayMap<String, Integer> assetPaths = new ArrayMap<>(originalAssetsCount);
+ for (int i = 0; i < originalAssetsCount; i++) {
+ assetPaths.put(mApkAssets[i].getAssetPath(), i);
}
- final ApkAssets assets;
- try {
- if (overlay) {
- // TODO(b/70343104): This hardcoded path will be removed once
- // addAssetPathInternal is deleted.
- final String idmapPath = "/data/resource-cache/"
- + path.substring(1).replace('/', '@')
- + "@idmap";
- assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */);
+ final var newKeys = new ArrayList<ApkKey>(apkKeys.size());
+ int lastFoundIndex = -1;
+ for (int i = 0, pathsSize = apkKeys.size(); i < pathsSize; i++) {
+ final var key = apkKeys.get(i);
+ final var index = assetPaths.get(key.path);
+ if (index == null) {
+ newKeys.add(key);
} else {
- assets = ApkAssets.loadFromPath(path,
- appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
+ lastFoundIndex = index;
}
- } catch (IOException e) {
- return 0;
+ }
+ if (newKeys.isEmpty()) {
+ return lastFoundIndex + 1;
}
- mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
- mApkAssets[count] = assets;
- nativeSetApkAssets(mObject, mApkAssets, true, false);
+ final var newAssets = loadAssets(newKeys);
+ if (newAssets.isEmpty()) {
+ return 0;
+ }
+ mApkAssets = makeNewAssetsArrayLocked(newAssets);
+ nativeSetApkAssets(mObject, mApkAssets, true, presetAssets);
invalidateCachesLocked(-1);
- return count + 1;
+ return originalAssetsCount + 1;
+ }
+ }
+
+ /**
+ * Insert the new assets preserving the correct order: all non-loader assets go before all
+ * of the loader assets.
+ */
+ @GuardedBy("this")
+ private @NonNull ApkAssets[] makeNewAssetsArrayLocked(
+ @NonNull ArrayList<ApkAssets> newNonLoaderAssets) {
+ final int originalAssetsCount = mApkAssets.length;
+ int firstLoaderIndex = originalAssetsCount;
+ for (int i = 0; i < originalAssetsCount; i++) {
+ if (mApkAssets[i].isForLoader()) {
+ firstLoaderIndex = i;
+ break;
+ }
+ }
+ final int newAssetsSize = newNonLoaderAssets.size();
+ final var newAssetsArray = new ApkAssets[originalAssetsCount + newAssetsSize];
+ if (firstLoaderIndex > 0) {
+ // This should always be true, but who knows...
+ System.arraycopy(mApkAssets, 0, newAssetsArray, 0, firstLoaderIndex);
+ }
+ for (int i = 0; i < newAssetsSize; i++) {
+ newAssetsArray[firstLoaderIndex + i] = newNonLoaderAssets.get(i);
+ }
+ if (originalAssetsCount > firstLoaderIndex) {
+ System.arraycopy(
+ mApkAssets, firstLoaderIndex,
+ newAssetsArray, firstLoaderIndex + newAssetsSize,
+ originalAssetsCount - firstLoaderIndex);
+ }
+ return newAssetsArray;
+ }
+
+ private static @NonNull ArrayList<ApkAssets> loadAssets(@NonNull ArrayList<ApkKey> keys) {
+ final int pathsSize = keys.size();
+ final var loadedAssets = new ArrayList<ApkAssets>(pathsSize);
+ final var resourcesManager = ResourcesManager.getInstance();
+ for (int i = 0; i < pathsSize; i++) {
+ final var key = keys.get(i);
+ try {
+ // ResourcesManager has a cache of loaded assets, ensuring we don't open the same
+ // file repeatedly, which is useful for the common overlays and registered
+ // shared libraries.
+ loadedAssets.add(resourcesManager.loadApkAssets(key));
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to load asset, key = " + key, e);
+ }
}
+ return loadedAssets;
}
/** @hide */
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 31cacb705d0e..c33eaeae1c45 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -29,7 +29,6 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.app.LocaleConfig;
import android.app.ResourcesManager;
-import android.app.ResourcesManager.SharedLibraryAssets;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
@@ -48,7 +47,6 @@ import android.os.Build;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
-import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -147,10 +145,9 @@ public class ResourcesImpl {
// Cyclical cache used for recently-accessed XML files.
private int mLastCachedXmlBlockIndex = -1;
- // The number of shared libraries registered within this ResourcesImpl, which is designed to
- // help to determine whether this ResourcesImpl is outdated on shared library information and
- // needs to be replaced.
- private int mSharedLibCount;
+ // The hash that allows to detect when the shared libraries applied to this object have changed,
+ // and it is outdated and needs to be replaced.
+ private final int mAppliedSharedLibsHash;
private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
@@ -204,15 +201,8 @@ public class ResourcesImpl {
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
- if (Flags.registerResourcePaths()) {
- ArrayMap<String, SharedLibraryAssets> sharedLibMap =
- ResourcesManager.getInstance().getSharedLibAssetsMap();
- final int size = sharedLibMap.size();
- for (int i = 0; i < size; i++) {
- assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths());
- }
- mSharedLibCount = sharedLibMap.size();
- }
+ mAppliedSharedLibsHash =
+ ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
@@ -1615,7 +1605,7 @@ public class ResourcesImpl {
}
}
- public int getSharedLibCount() {
- return mSharedLibCount;
+ public int getAppliedSharedLibsHash() {
+ return mAppliedSharedLibsHash;
}
-}
+} \ No newline at end of file
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index b98a0d825227..c521b96fd8ee 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -232,7 +232,17 @@ public class IntentFactory {
oemComponentName,
PackageManager.ComponentInfoFlags.of(
PackageManager.MATCH_SYSTEM_ONLY));
- if (info.enabled && info.exported) {
+ boolean oemComponentEnabled = info.enabled;
+ int runtimeComponentEnabledState = context.getPackageManager()
+ .getComponentEnabledSetting(oemComponentName);
+ if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_ENABLED) {
+ oemComponentEnabled = true;
+ } else if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_DISABLED) {
+ oemComponentEnabled = false;
+ }
+ if (oemComponentEnabled && info.exported) {
intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
.OemUiUsageStatus.SUCCESS);
Slog.i(TAG,
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 6fffb822c9ca..056ca93159dd 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -564,7 +564,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, then this list will only contain
* CONTROL_ZOOM_RATIO_RANGE and SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
*
- * @see INFO_SESSION_CONFIGURATION_QUERY_VERSION
+ * @see #INFO_SESSION_CONFIGURATION_QUERY_VERSION
*/
@NonNull
@FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6968f279dbc0..fbed50ad3c2d 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -57,7 +57,7 @@ import java.util.Set;
*
* <p>CaptureRequests can be created by using a {@link Builder} instance,
* obtained by calling {@link CameraDevice#createCaptureRequest} or {@link
- * CameraManager#createCaptureRequest}</p>
+ * CameraDevice.CameraDeviceSetup#createCaptureRequest}</p>
*
* <p>CaptureRequests are given to {@link CameraCaptureSession#capture} or
* {@link CameraCaptureSession#setRepeatingRequest} to capture images from a camera.</p>
@@ -84,7 +84,7 @@ import java.util.Set;
* @see CameraCaptureSession#setRepeatingBurst
* @see CameraDevice#createCaptureRequest
* @see CameraDevice#createReprocessCaptureRequest
- * @see CameraManager#createCaptureRequest
+ * @see CameraDevice.CameraDeviceSetup#createCaptureRequest
*/
public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
implements Parcelable {
@@ -812,8 +812,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* A builder for capture requests.
*
* <p>To obtain a builder instance, use the
- * {@link CameraDevice#createCaptureRequest} or {@link CameraManager#createCaptureRequest}
- * method, which initializes the request fields to one of the templates defined in
+ * {@link CameraDevice#createCaptureRequest} or
+ * {@link CameraDevice.CameraDeviceSetup#createCaptureRequest} method, which
+ * initializes the request fields to one of the templates defined in
* {@link CameraDevice}.
*
* @see CameraDevice#createCaptureRequest
@@ -822,7 +823,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @see CameraDevice#TEMPLATE_STILL_CAPTURE
* @see CameraDevice#TEMPLATE_VIDEO_SNAPSHOT
* @see CameraDevice#TEMPLATE_MANUAL
- * @see CameraManager#createCaptureRequest
+ * @see CameraDevice.CameraDeviceSetup#createCaptureRequest
*/
public final static class Builder {
diff --git a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
index 69a6e9b0ab32..bf3f59fc7480 100644
--- a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
@@ -23,12 +23,13 @@ import android.graphics.ColorSpace;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraExtensionCharacteristics.Extension;
import android.hardware.camera2.CameraExtensionSession;
+import android.media.ImageReader;
+
+import com.android.internal.camera.flags.Flags;
import java.util.List;
import java.util.concurrent.Executor;
-import com.android.internal.camera.flags.Flags;
-
/**
* A class that aggregates all supported arguments for
* {@link CameraExtensionSession} initialization.
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index f3b7b919d87d..fb2c2f036b42 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -260,7 +260,7 @@ public final class MandatoryStreamCombination {
* smaller sizes, then the resulting
* {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
* be tested either by calling {@link CameraDevice#createCaptureSession} or
- * {@link CameraDeviceSetup#isSessionConfigurationSupported}.
+ * {@link CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}.
*
* @return non-modifiable ascending list of available sizes.
*/
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 3b2913c81d49..0c55ed5323a0 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -130,8 +130,8 @@ public final class SessionConfiguration implements Parcelable {
* Create a new {@link SessionConfiguration} with sessionType and output configurations.
*
* <p>The SessionConfiguration objects created by this constructor can be used by
- * {@link CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} to query a camera device's feature
+ * {@link CameraDeviceSetup#isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup#getSessionCharacteristics} to query a camera device's feature
* combination support and session specific characteristics. For the SessionConfiguration
* object to be used to create a capture session, {@link #setStateCallback} must be called to
* specify the state callback function, and any incomplete OutputConfigurations must be
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 2ded615580fd..903e91646332 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -699,6 +699,25 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * Set whether the HAL should ignore display touches.
+ * Only applies to sensors where the HAL is reponsible for handling touches.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouch) {
+ if (mService == null) {
+ Slog.w(TAG, "setIgnoreDisplayTouches: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouch);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index f701ec3ec367..d84d29292ed5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -120,6 +120,14 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
}
/**
+ * Returns if sensor type is ultrasonic Udfps
+ * @return true if sensor is ultrasonic Udfps, false otherwise
+ */
+ public boolean isUltrasonicUdfps() {
+ return sensorType == TYPE_UDFPS_ULTRASONIC;
+ }
+
+ /**
* Returns if sensor type is side-FPS
* @return true if sensor is side-fps, false otherwise
*/
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 742fa570bad7..370f097b6850 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -195,6 +195,9 @@ interface IFingerprintService {
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void onUdfpsUiEvent(int event, long requestId, int sensorId);
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches);
+
// Sets the controller for managing the UDFPS overlay.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d91508fe1612..5c392ae6bd2c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11066,6 +11066,13 @@ public final class Settings {
"active_unlock_on_biometric_fail";
/**
+ * Whether or not active unlock triggers on legacy unlock intents.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY =
+ "active_unlock_on_unlock_intent_legacy";
+
+ /**
* If active unlock triggers on biometric failures, include the following error codes
* as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
* Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index d6425c397bbb..ed20207cfb0b 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -32,3 +32,14 @@ flag {
description: "Provides additional callbacks with information about user actions in ChooserResult"
bug: "263474465"
}
+
+flag {
+ name: "fix_resolver_memory_leak"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "ResolverActivity memory leak (through the AppPredictor callback) fix"
+ bug: "346671041"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 83ff88bdcc1c..1b3b3ebbecfc 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -89,7 +89,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
t.apply();
t.close();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2377b869e804..dde4b34ea655 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -126,6 +126,7 @@ import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodCl
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
+import static com.android.window.flags.Flags.insetsControlChangedItem;
import static com.android.window.flags.Flags.setScPropertiesInClient;
import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
@@ -1266,7 +1267,6 @@ public final class ViewRootImpl implements ViewParent,
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
- // TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
mChoreographer = Choreographer.getInstance();
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mImeBackAnimationController = new ImeBackAnimationController(this, mInsetsController);
@@ -4405,7 +4405,7 @@ public final class ViewRootImpl implements ViewParent,
if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
mAppStartTrackingStarted = true;
Transaction transaction = new Transaction();
- transaction.addTransactionCompletedListener(mExecutor,
+ transaction.addTransactionCompletedListener(mSimpleExecutor,
new Consumer<TransactionStats>() {
@Override
public void accept(TransactionStats transactionStats) {
@@ -11417,8 +11417,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl.Array activeControls) {
- final boolean isFromInsetsControlChangeItem = mIsFromTransactionItem;
- mIsFromTransactionItem = false;
+ final boolean isFromInsetsControlChangeItem;
+ if (insetsControlChangedItem()) {
+ isFromInsetsControlChangeItem = mIsFromTransactionItem;
+ mIsFromTransactionItem = false;
+ } else {
+ isFromInsetsControlChangeItem = false;
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor == null) {
if (isFromInsetsControlChangeItem) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 8bd39fbe3a52..939ba29b71ad 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -205,7 +205,7 @@ public final class TransitionInfo implements Parcelable {
FLAG_SYNC,
FLAG_CONFIG_AT_END,
FLAG_FIRST_CUSTOM
- })
+ }, flag = true)
public @interface ChangeFlags {}
private final @TransitionType int mType;
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index b71468247e37..985dc102b31b 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -192,4 +192,15 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "ensure_wallpaper_in_transitions"
+ namespace: "windowing_frontend"
+ description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
+ bug: "347593088"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 0a4762d91610..552cb7a1b978 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -191,3 +191,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "fix_no_container_update_without_resize"
+ description: "Fix the containers not being updated when the Task is brought to front and has the same configuration"
+ bug: "344721335"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 920981e2b8fe..a1945352ae09 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1209,9 +1209,19 @@ public class ResolverActivity extends Activity implements
if (!isChangingConfigurations() && mPickOptionRequest != null) {
mPickOptionRequest.cancel();
}
- if (mMultiProfilePagerAdapter != null
- && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
- mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
+ if (mMultiProfilePagerAdapter != null) {
+ ResolverListAdapter activeAdapter =
+ mMultiProfilePagerAdapter.getActiveListAdapter();
+ if (activeAdapter != null) {
+ activeAdapter.onDestroy();
+ }
+ if (android.service.chooser.Flags.fixResolverMemoryLeak()) {
+ ResolverListAdapter inactiveAdapter =
+ mMultiProfilePagerAdapter.getInactiveListAdapter();
+ if (inactiveAdapter != null) {
+ inactiveAdapter.onDestroy();
+ }
+ }
}
}
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index e983427129c8..0f4f219f7e24 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -393,6 +393,12 @@
<bool name="config_wait_for_device_alignment_in_demo_datagram">false</bool>
<java-symbol type="bool" name="config_wait_for_device_alignment_in_demo_datagram" />
+ <!-- Boolean indicating whether to enable MMS to be attempted on IWLAN if possible, even if
+ existing cellular networks already supports IWLAN.
+ -->
+ <bool name="force_iwlan_mms_feature_enabled">false</bool>
+ <java-symbol type="bool" name="force_iwlan_mms_feature_enabled" />
+
<!-- The time duration in millis after which Telephony will abort the last message datagram
sending requests. Telephony starts a timer when receiving a last message datagram sending
request in either OFF, IDLE, or NOT_CONNECTED state. In NOT_CONNECTED, the duration of the
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 10373a59d819..1a2f0ccb06ba 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6503,17 +6503,17 @@ ul.</string>
<!-- Fingerprint dangling notification title -->
<string name="fingerprint_dangling_notification_title">Set up Fingerprint Unlock again</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted -->
- <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted</string>
+ <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized.</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted -->
- <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted</string>
+ <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized.</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted and no fingerprint left-->
- <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted and no fingerprint left -->
- <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint.</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string>
<!-- Face dangling notification title -->
<string name="face_dangling_notification_title">Set up Face Unlock again</string>
<!-- Face dangling notification content -->
- <string name="face_dangling_notification_msg">Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with face.</string>
+ <string name="face_dangling_notification_msg">Your face model can no longer be recognized. Set up Face Unlock again.</string>
<!-- Biometric dangling notification "set up" action button -->
<string name="biometric_dangling_notification_action_set_up">Set up</string>
<!-- Biometric dangling notification "Not now" action button -->
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 6b3cf7bb628f..ee1b6589f7e6 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -44,7 +44,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -382,8 +381,7 @@ public class ResourcesManagerTest extends TestCase {
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
@@ -414,9 +412,7 @@ public class ResourcesManagerTest extends TestCase {
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
@@ -452,9 +448,7 @@ public class ResourcesManagerTest extends TestCase {
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
@@ -493,9 +487,7 @@ public class ResourcesManagerTest extends TestCase {
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 66b47dae1b3e..a115c659d85a 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -40,6 +40,7 @@
<permission name="android.permission.MASTER_CLEAR"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <permission name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index f78e2b5170fc..09625151fda4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -899,14 +899,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) {
final TaskContainer taskContainer = getTaskContainer(taskId);
- if (taskContainer != null && taskContainer.isVisible()) {
+ if (taskContainer == null) {
+ return;
+ }
+
+ if (taskContainer.isVisible()) {
updateContainersInTask(wct, taskContainer);
+ } else if (Flags.fixNoContainerUpdateWithoutResize()) {
+ // the TaskFragmentContainers need to be updated when the task becomes visible
+ taskContainer.mTaskFragmentContainersNeedsUpdate = true;
}
}
@GuardedBy("mLock")
private void updateContainersInTask(@NonNull WindowContainerTransaction wct,
@NonNull TaskContainer taskContainer) {
+ taskContainer.mTaskFragmentContainersNeedsUpdate = false;
+
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index ee00c4cd67eb..20ad53ee19a8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -108,6 +108,12 @@ class TaskContainer {
private boolean mPlaceholderRuleSuppressed;
/**
+ * {@code true} if the TaskFragments in this Task needs to be updated next time the Task
+ * becomes visible. See {@link #shouldUpdateContainer(TaskFragmentParentInfo)}
+ */
+ boolean mTaskFragmentContainersNeedsUpdate;
+
+ /**
* The {@link TaskContainer} constructor
*
* @param taskId The ID of the Task, which must match {@link Activity#getTaskId()} with
@@ -185,7 +191,8 @@ class TaskContainer {
// If the task properties equals regardless of starting position, don't
// need to update the container.
- return mInfo.getConfiguration().diffPublicOnly(configuration) != 0
+ return mTaskFragmentContainersNeedsUpdate
+ || mInfo.getConfiguration().diffPublicOnly(configuration) != 0
|| mInfo.getDisplayId() != info.getDisplayId();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index a3111b31a2f9..169e122c353c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -61,8 +61,7 @@ abstract class CrossActivityBackAnimation(
private val context: Context,
private val background: BackAnimationBackground,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- protected val transaction: SurfaceControl.Transaction,
- private val choreographer: Choreographer
+ protected val transaction: SurfaceControl.Transaction
) : ShellBackAnimation() {
protected val startClosingRect = RectF()
@@ -269,7 +268,9 @@ abstract class CrossActivityBackAnimation(
.setSpring(postCommitFlingSpring)
flingAnimation.start()
// do an animation-frame immediately to prevent idle frame
- flingAnimation.doAnimationFrame(choreographer.lastFrameTimeNanos / TimeUtils.NANOS_PER_MS)
+ flingAnimation.doAnimationFrame(
+ Choreographer.getInstance().lastFrameTimeNanos / TimeUtils.NANOS_PER_MS
+ )
val valueAnimator =
ValueAnimator.ofFloat(1f, 0f).setDuration(getPostCommitAnimationDuration())
@@ -324,6 +325,7 @@ abstract class CrossActivityBackAnimation(
enteringHasSameLetterbox = false
lastPostCommitFlingScale = SPRING_SCALE
gestureProgress = 0f
+ triggerBack = false
}
protected fun applyTransform(
@@ -361,7 +363,7 @@ abstract class CrossActivityBackAnimation(
}
protected fun applyTransaction() {
- transaction.setFrameTimelineVsync(choreographer.vsyncId)
+ transaction.setFrameTimelineVsync(Choreographer.getInstance().vsyncId)
transaction.apply()
}
@@ -499,10 +501,12 @@ abstract class CrossActivityBackAnimation(
}
override fun onBackCancelled() {
+ triggerBack = false
progressAnimator.onBackCancelled { finishAnimation() }
}
override fun onBackInvoked() {
+ triggerBack = true
progressAnimator.reset()
onGestureCommitted(progressAnimator.velocity)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index c738ce542f8a..9ebab6383416 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -19,7 +19,6 @@ import android.content.Context
import android.graphics.Rect
import android.graphics.RectF
import android.util.MathUtils
-import android.view.Choreographer
import android.view.SurfaceControl
import android.view.animation.Animation
import android.view.animation.Transformation
@@ -31,27 +30,23 @@ import com.android.internal.policy.TransitionAnimation
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
-import com.android.wm.shell.shared.annotations.ShellMainThread
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
/** Class that handles customized predictive cross activity back animations. */
-@ShellMainThread
class CustomCrossActivityBackAnimation(
context: Context,
background: BackAnimationBackground,
rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
transaction: SurfaceControl.Transaction,
- choreographer: Choreographer,
private val customAnimationLoader: CustomAnimationLoader
) :
CrossActivityBackAnimation(
context,
background,
rootTaskDisplayAreaOrganizer,
- transaction,
- choreographer
+ transaction
) {
private var enterAnimation: Animation? = null
@@ -70,7 +65,6 @@ class CustomCrossActivityBackAnimation(
background,
rootTaskDisplayAreaOrganizer,
SurfaceControl.Transaction(),
- Choreographer.getInstance(),
CustomAnimationLoader(
TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation")
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index 3b5eb3613d2a..c747e1e98956 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -16,18 +16,15 @@
package com.android.wm.shell.back
import android.content.Context
-import android.view.Choreographer
import android.view.SurfaceControl
import android.window.BackEvent
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.animation.Interpolators
-import com.android.wm.shell.shared.annotations.ShellMainThread
import javax.inject.Inject
import kotlin.math.max
/** Class that defines cross-activity animation. */
-@ShellMainThread
class DefaultCrossActivityBackAnimation
@Inject
constructor(
@@ -39,8 +36,7 @@ constructor(
context,
background,
rootTaskDisplayAreaOrganizer,
- SurfaceControl.Transaction(),
- Choreographer.getInstance()
+ SurfaceControl.Transaction()
) {
private val postCommitInterpolator = Interpolators.EMPHASIZED
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 6ffeb97f50fa..58007b50350b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -27,7 +27,9 @@ import android.util.DisplayMetrics;
import android.util.Size;
import android.view.Gravity;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -39,6 +41,9 @@ public class PipBoundsAlgorithm {
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
+ // The same value (with the same name) is used in Launcher.
+ private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.01f;
+
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull protected final PipDisplayLayoutState mPipDisplayLayoutState;
@NonNull protected final SizeSpecSource mSizeSpecSource;
@@ -206,9 +211,27 @@ public class PipBoundsAlgorithm {
*/
public static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint,
Rect destinationBounds) {
- return sourceRectHint != null
- && sourceRectHint.width() > destinationBounds.width()
- && sourceRectHint.height() > destinationBounds.height();
+ if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, empty hint");
+ return false;
+ }
+ if (sourceRectHint.width() <= destinationBounds.width()
+ || sourceRectHint.height() <= destinationBounds.height()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, hint(%s) is smaller"
+ + " than destination(%s)", sourceRectHint, destinationBounds);
+ return false;
+ }
+ final float reportedRatio = destinationBounds.width() / (float) destinationBounds.height();
+ final float inferredRatio = sourceRectHint.width() / (float) sourceRectHint.height();
+ if (Math.abs(reportedRatio - inferredRatio) > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, hint(%s) does not match"
+ + " destination(%s) aspect ratio", sourceRectHint, destinationBounds);
+ return false;
+ }
+ return true;
}
public float getDefaultAspectRatio() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index de016d3ae400..5097ed8866c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -269,7 +269,9 @@ public class SplitDecorManager extends WindowlessWindowManager {
if (update) {
if (immediately) {
+ t.setAlpha(mBackgroundLeash, showVeil ? 1f : 0f);
t.setVisibility(mBackgroundLeash, showVeil);
+ t.setAlpha(mIconLeash, showVeil ? 1f : 0f);
t.setVisibility(mIconLeash, showVeil);
} else {
startFadeAnimation(showVeil, false, null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index bfac24b81d2f..2520c25613e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -258,9 +258,15 @@ public class CompatUIController implements OnDisplaysChangedListener,
return;
}
// We're showing the first reachability education so we ignore incoming TaskInfo
- // until the education flow has completed or we double tap.
+ // until the education flow has completed or we double tap. The double-tap
+ // basically cancel all the onboarding flow. We don't have to ignore events in case
+ // the app is in size compat mode.
if (mIsFirstReachabilityEducationRunning) {
- return;
+ if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap
+ && !taskInfo.appCompatTaskInfo.topActivityInSizeCompat) {
+ return;
+ }
+ mIsFirstReachabilityEducationRunning = false;
}
if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
@@ -278,17 +284,24 @@ public class CompatUIController implements OnDisplaysChangedListener,
final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
&& !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
- mIsFirstReachabilityEducationRunning = true;
mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
- return;
+ // We activate the first reachability education if the double-tap is enabled.
+ // If the double tap is not enabled (e.g. thin letterbox) we just set the value
+ // of the education being seen.
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ mIsFirstReachabilityEducationRunning = true;
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ return;
+ }
}
}
}
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ }
// The user aspect ratio button should not be handled when a new TaskInfo is
// sent because of a double tap or when in multi-window mode.
if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e4420d73886e..3ba27961b713 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,7 +63,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.Rational;
import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
@@ -128,8 +127,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
SystemProperties.getInt(
"persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400);
- private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.005f;
-
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -821,37 +818,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPictureInPictureParams.getTitle());
mPipParamsChangedForwarder.notifySubtitleChanged(
mPictureInPictureParams.getSubtitle());
-
- if (mPictureInPictureParams.hasSourceBoundsHint()
- && mPictureInPictureParams.hasSetAspectRatio()) {
- Rational sourceRectHintAspectRatio = new Rational(
- mPictureInPictureParams.getSourceRectHint().width(),
- mPictureInPictureParams.getSourceRectHint().height());
- if (sourceRectHintAspectRatio.compareTo(
- mPictureInPictureParams.getAspectRatio()) != 0) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "Aspect ratio of source rect hint (%d/%d) does not match the provided "
- + "aspect ratio value (%d/%d). Consider matching them for "
- + "improved animation. Future releases might override the "
- + "value to match.",
- mPictureInPictureParams.getSourceRectHint().width(),
- mPictureInPictureParams.getSourceRectHint().height(),
- mPictureInPictureParams.getAspectRatio().getNumerator(),
- mPictureInPictureParams.getAspectRatio().getDenominator());
- }
- if (Math.abs(sourceRectHintAspectRatio.floatValue()
- - mPictureInPictureParams.getAspectRatioFloat())
- > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "Aspect ratio of source rect hint (%f) does not match the provided "
- + "aspect ratio value (%f) and is above threshold of %f. "
- + "Consider matching them for improved animation. Future "
- + "releases might override the value to match.",
- sourceRectHintAspectRatio.floatValue(),
- mPictureInPictureParams.getAspectRatioFloat(),
- PIP_ASPECT_RATIO_MISMATCH_THRESHOLD);
- }
- }
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 0541a0287179..b31ef2b6ad4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -68,6 +68,7 @@ class SplitScreenTransitions {
DismissSession mPendingDismiss = null;
EnterSession mPendingEnter = null;
TransitSession mPendingResize = null;
+ TransitSession mPendingRemotePassthrough = null;
private IBinder mAnimatingTransition = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
@@ -320,6 +321,11 @@ class SplitScreenTransitions {
return mPendingResize != null && mPendingResize.mTransition == transition;
}
+ boolean isPendingPassThrough(IBinder transition) {
+ return mPendingRemotePassthrough != null &&
+ mPendingRemotePassthrough.mTransition == transition;
+ }
+
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
@@ -331,6 +337,9 @@ class SplitScreenTransitions {
} else if (isPendingResize(transition)) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
return mPendingResize;
+ } else if (isPendingPassThrough(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved passThrough transition");
+ return mPendingRemotePassthrough;
}
return null;
}
@@ -378,6 +387,19 @@ class SplitScreenTransitions {
extraTransitType, resizeAnim);
}
+ /** Sets a transition to enter split. */
+ void setRemotePassThroughTransition(@NonNull IBinder transition,
+ @Nullable RemoteTransition remoteTransition) {
+ mPendingRemotePassthrough = new TransitSession(
+ transition, null, null,
+ remoteTransition, Transitions.TRANSIT_SPLIT_PASSTHROUGH);
+
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced remote passthrough split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setRemotePassThrough: transitType=%d remote=%s",
+ Transitions.TRANSIT_SPLIT_PASSTHROUGH, remoteTransition);
+ }
+
/** Starts a transition to dismiss split. */
IBinder startDismissTransition(WindowContainerTransaction wct,
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@@ -474,6 +496,12 @@ class SplitScreenTransitions {
mPendingResize.onConsumed(aborted);
mPendingResize = null;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
+ } else if (isPendingPassThrough(transition)) {
+ mPendingRemotePassthrough.onConsumed(aborted);
+ mPendingRemotePassthrough.mRemoteHandler.onTransitionConsumed(transition, aborted,
+ finishT);
+ mPendingRemotePassthrough = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
}
// TODO: handle transition consumed for active remote handler
@@ -495,6 +523,10 @@ class SplitScreenTransitions {
mPendingResize.onFinished(wct, mFinishTransaction);
mPendingResize = null;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
+ } else if (isPendingPassThrough(mAnimatingTransition)) {
+ mPendingRemotePassthrough.onFinished(wct, mFinishTransaction);
+ mPendingRemotePassthrough = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for passThrough transition");
}
mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b6a18e537600..4287daa03223 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2659,6 +2659,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
&& displayChange.getStartRotation() != displayChange.getEndRotation()) {
mSplitLayout.setFreezeDividerWindow(true);
}
+ if (request.getRemoteTransition() != null) {
+ mSplitTransitions.setRemotePassThroughTransition(transition,
+ request.getRemoteTransition());
+ }
// Still want to monitor everything while in split-screen, so return non-null.
return new WindowContainerTransaction();
} else {
@@ -2985,6 +2989,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
notifySplitAnimationFinished();
return true;
}
+ } else if (mSplitTransitions.isPendingPassThrough(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startAnimation: passThrough transition=%d", info.getDebugId());
+ mSplitTransitions.mPendingRemotePassthrough.mRemoteHandler.startAnimation(transition,
+ info, startTransaction, finishTransaction, finishCallback);
+ notifySplitAnimationFinished();
+ return true;
}
return startPendingAnimation(transition, info, startTransaction, finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index f257e207673d..d2e6a12c583c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -28,12 +28,16 @@ import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -73,6 +77,7 @@ import androidx.annotation.BinderThread;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -184,6 +189,9 @@ public class Transitions implements RemoteCallable<Transitions>,
// TRANSIT_FIRST_CUSTOM + 17
TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE;
+ /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
+ public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
+
/** Transition type for desktop mode transitions. */
public static final int TRANSIT_DESKTOP_MODE_TYPES =
WindowManager.TRANSIT_FIRST_CUSTOM + 100;
@@ -512,12 +520,17 @@ public class Transitions implements RemoteCallable<Transitions>,
boolean isOpening = isOpeningType(info.getType());
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
+ if (change.hasFlags(FLAGS_IS_NON_APP_WINDOW & ~FLAG_IS_WALLPAPER)) {
// Currently system windows are controlled by WindowState, so don't change their
// surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
- // This includes Wallpaper (always z-ordered at bottom) and IME (associated with
- // app), because there may not be a transition associated with their visibility
- // changes, and currently they don't need transition animation.
+ // This includes IME (associated with app), because there may not be a transition
+ // associated with their visibility changes, and currently they don't need a
+ // transition animation.
+ continue;
+ }
+ if (change.hasFlags(FLAG_IS_WALLPAPER) && !ensureWallpaperInTransitions()) {
+ // Wallpaper is always z-ordered at bottom, and historically is not animated by
+ // transition handlers.
continue;
}
final SurfaceControl leash = change.getLeash();
@@ -570,6 +583,14 @@ public class Transitions implements RemoteCallable<Transitions>,
final boolean isOpening = isOpeningType(transitType);
final boolean isClosing = isClosingType(transitType);
final int mode = change.getMode();
+ // Ensure wallpapers stay in the back
+ if (change.hasFlags(FLAG_IS_WALLPAPER) && Flags.ensureWallpaperInTransitions()) {
+ if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
+ return -zSplitLine + numChanges - i;
+ } else {
+ return -zSplitLine - i;
+ }
+ }
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
@@ -788,14 +809,16 @@ public class Transitions implements RemoteCallable<Transitions>,
final int changeSize = info.getChanges().size();
boolean taskChange = false;
boolean transferStartingWindow = false;
- int noAnimationBehindStartingWindow = 0;
+ int animBehindStartingWindow = 0;
boolean allOccluded = changeSize > 0;
for (int i = changeSize - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
taskChange |= change.getTaskInfo() != null;
transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
- if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)) {
- noAnimationBehindStartingWindow++;
+ if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)
+ || change.hasAllFlags(
+ FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ animBehindStartingWindow++;
}
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
@@ -813,11 +836,11 @@ public class Transitions implements RemoteCallable<Transitions>,
// There does not need animation when:
// A. Transfer starting window. Apply transfer starting window directly if there is no other
// task change. Since this is an activity->activity situation, we can detect it by selecting
- // transitions with only 2 changes where
- // 1. neither are tasks, and
+ // transitions with changes where
+ // 1. none are tasks, and
// 2. one is a starting-window recipient, or all change is behind starting window.
- if (!taskChange && (transferStartingWindow || noAnimationBehindStartingWindow == changeSize)
- && changeSize == 2
+ if (!taskChange && (transferStartingWindow || animBehindStartingWindow == changeSize)
+ && changeSize >= 1
// B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
// changes are underneath another change.
|| ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 8bf011192347..080ad901c656 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -25,7 +25,6 @@ import android.graphics.Rect
import android.os.RemoteException
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.view.Choreographer
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
@@ -37,8 +36,6 @@ import androidx.test.filters.SmallTest
import com.android.internal.policy.TransitionAnimation
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
import junit.framework.TestCase.assertEquals
import org.junit.Assert
import org.junit.Before
@@ -50,12 +47,13 @@ import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
@SmallTest
@TestableLooper.RunWithLooper
@@ -82,7 +80,6 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
backAnimationBackground,
rootTaskDisplayAreaOrganizer,
transaction,
- mock(Choreographer::class.java),
customAnimationLoader
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 34b2eebb15a1..ae226b807d13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -50,8 +50,10 @@ import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.os.IBinder;
+import android.os.RemoteException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.IRemoteTransition;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -326,6 +328,32 @@ public class SplitTransitionTests extends ShellTestCase {
@Test
@UiThreadTest
+ public void testRemotePassThroughInvoked() throws RemoteException {
+ RemoteTransition remoteWrapper = mock(RemoteTransition.class);
+ IRemoteTransition remoteTransition = mock(IRemoteTransition.class);
+ IBinder remoteBinder = mock(IBinder.class);
+ doReturn(remoteBinder).when(remoteTransition).asBinder();
+ doReturn(remoteTransition).when(remoteWrapper).getRemoteTransition();
+
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_CHANGE, null,
+ remoteWrapper);
+ IBinder transition = mock(IBinder.class);
+ mMainStage.activate(new WindowContainerTransaction(), false);
+ mStageCoordinator.handleRequest(transition, request);
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .build();
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+
+ verify(remoteTransition, times(1)).startAnimation(any(),
+ any(), any(), any());
+ }
+
+ @Test
+ @UiThreadTest
public void testEnterRecentsAndRestore() {
enterSplit();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 386a606c61c6..a4d0c1714e23 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7005,6 +7005,23 @@ public class AudioManager {
}
/**
+ * Check whether a user can mute this stream type from a given UI element.
+ *
+ * <p>Only useful for volume controllers.
+ *
+ * @param streamType type of stream to check if it's mutable from UI
+ *
+ * @hide
+ */
+ public boolean isStreamMutableByUi(int streamType) {
+ try {
+ return getService().isStreamMutableByUi(streamType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Only useful for volume controllers.
* @hide
*/
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c8b9da50b082..08cc126fb1a8 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -305,6 +305,8 @@ interface IAudioService {
boolean isStreamAffectedByMute(int streamType);
+ boolean isStreamMutableByUi(int streamType);
+
void disableSafeMediaVolume(String callingPackage);
oneway void lowerVolumeToRs1(String callingPackage);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 0589c0f12ab6..e048d5c56cfe 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -26,6 +26,7 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
@@ -813,6 +814,34 @@ public final class MediaRoute2Info implements Parcelable {
|| mAllowedPackages.contains(packageName);
}
+ /**
+ * Returns whether this route's type can only be published by the system route provider.
+ *
+ * @see #isSystemRoute()
+ * @hide
+ */
+ // The default case catches all other types.
+ @SuppressLint("SwitchIntDef")
+ public boolean isSystemRouteType() {
+ return switch (mType) {
+ case TYPE_BUILTIN_SPEAKER,
+ TYPE_BLUETOOTH_A2DP,
+ TYPE_DOCK,
+ TYPE_BLE_HEADSET,
+ TYPE_HEARING_AID,
+ TYPE_HDMI,
+ TYPE_HDMI_ARC,
+ TYPE_HDMI_EARC,
+ TYPE_USB_ACCESSORY,
+ TYPE_USB_DEVICE,
+ TYPE_USB_HEADSET,
+ TYPE_WIRED_HEADPHONES,
+ TYPE_WIRED_HEADSET ->
+ true;
+ default -> false;
+ };
+ }
+
/** Returns the route suitability status. */
@SuitabilityStatus
@FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index cce3d4fc67d5..a14f1fd15f27 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -475,9 +475,25 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
public final void notifyRoutes(@NonNull Collection<MediaRoute2Info> routes) {
Objects.requireNonNull(routes, "routes must not be null");
- mProviderInfo = new MediaRoute2ProviderInfo.Builder()
- .addRoutes(routes)
- .build();
+ List<MediaRoute2Info> sanitizedRoutes = new ArrayList<>(routes.size());
+
+ for (MediaRoute2Info route : routes) {
+ if (route.isSystemRouteType()) {
+ Log.w(
+ TAG,
+ "Attempting to add a system route type from a non-system route "
+ + "provider. Overriding type to TYPE_UNKNOWN. Route: "
+ + route);
+ sanitizedRoutes.add(
+ new MediaRoute2Info.Builder(route)
+ .setType(MediaRoute2Info.TYPE_UNKNOWN)
+ .build());
+ } else {
+ sanitizedRoutes.add(route);
+ }
+ }
+
+ mProviderInfo = new MediaRoute2ProviderInfo.Builder().addRoutes(sanitizedRoutes).build();
schedulePublishState();
}
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 524a88a638ad..affcca1ccaed 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -34,7 +34,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/message_staging" />
<ProgressBar
@@ -57,7 +57,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/installing" />
<ProgressBar
@@ -74,7 +74,7 @@
android:id="@+id/install_confirm_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_confirm_question"
android:visibility="invisible"
android:scrollbars="vertical" />
@@ -83,7 +83,7 @@
android:id="@+id/install_confirm_question_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_confirm_question_update"
android:visibility="invisible"
android:scrollbars="vertical" />
@@ -92,7 +92,7 @@
android:id="@+id/install_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_done"
android:visibility="invisible" />
@@ -100,7 +100,7 @@
android:id="@+id/install_failed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_failed"
android:visibility="invisible" />
@@ -108,7 +108,7 @@
android:id="@+id/install_failed_blocked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_failed_blocked"
android:visibility="invisible" />
@@ -116,7 +116,7 @@
android:id="@+id/install_failed_conflict"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_failed_conflict"
android:visibility="invisible" />
@@ -124,7 +124,7 @@
android:id="@+id/install_failed_incompatible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_failed_incompatible"
android:visibility="invisible" />
@@ -132,7 +132,7 @@
android:id="@+id/install_failed_invalid_apk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle"
android:text="@string/install_failed_invalid_apk"
android:visibility="invisible" />
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 434e33323ba1..d5c5d8b986c6 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -22,32 +22,32 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/alertDialogTheme"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:clipToPadding="false">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
- <TextView
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle" />
- <CheckBox
- android:id="@+id/keepData"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="-8dp"
- android:paddingLeft="8sp"
- android:visibility="gone"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <CheckBox
+ android:id="@+id/keepData"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingStart="8sp"
+ android:paddingEnd="0sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.DeviceDefault.SearchResult.Subtitle" />
- </LinearLayout>
+ </LinearLayout>
</ScrollView>
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index a5b82b39344c..37588ad89221 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -19,7 +19,6 @@
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
- <item name="alertDialogStyle">@style/AlertDialog</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowAnimationStyle">@null</item>
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index f5af510111cd..aa487122994e 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -19,7 +19,6 @@
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
- <item name="alertDialogStyle">@style/AlertDialog</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowAnimationStyle">@null</item>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index e0398aa49dc9..824dd4a5fdaf 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -330,7 +330,8 @@ public class PackageInstallerActivity extends Activity {
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.MATCH_UNINSTALLED_PACKAGES);
- if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ // If the package is archived, treat it as update case.
+ if (!mAppInfo.isArchived && (mAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 2e9b7b421a7b..a23df64f3582 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -97,6 +97,7 @@ class InstallRepository(private val context: Context) {
private set
private var callingUid = Process.INVALID_UID
private var originatingUid = Process.INVALID_UID
+ private var originatingUidFromSessionInfo = Process.INVALID_UID
private var callingPackage: String? = null
private var sessionStager: SessionStager? = null
private lateinit var intent: Intent
@@ -136,17 +137,25 @@ class InstallRepository(private val context: Context) {
callingPackage = callerInfo.packageName
- if (sessionId != SessionInfo.INVALID_ID) {
- val sessionInfo: SessionInfo? = packageInstaller.getSessionInfo(sessionId)
- callingPackage = sessionInfo?.getInstallerPackageName()
- callingAttributionTag = sessionInfo?.getInstallerAttributionTag()
- }
-
// Uid of the source package, coming from ActivityManager
callingUid = callerInfo.uid
if (callingUid == Process.INVALID_UID) {
Log.e(LOG_TAG, "Could not determine the launching uid.")
}
+
+ originatingUidFromSessionInfo = callingUid
+ val sessionInfo: SessionInfo? =
+ if (sessionId != SessionInfo.INVALID_ID)
+ packageInstaller.getSessionInfo(sessionId)
+ else null
+ if (sessionInfo != null) {
+ callingPackage = sessionInfo.installerPackageName
+ callingAttributionTag = sessionInfo.installerAttributionTag
+ if (sessionInfo.originatingUid != Process.INVALID_UID) {
+ originatingUidFromSessionInfo = sessionInfo.originatingUid
+ }
+ }
+
val sourceInfo: ApplicationInfo? = getSourceInfo(callingPackage)
// Uid of the source package, with a preference to uid from ApplicationInfo
originatingUid = sourceInfo?.uid ?: callingUid
@@ -651,7 +660,12 @@ class InstallRepository(private val context: Context) {
private fun getUpdateMessage(pkgInfo: PackageInfo, userActionReason: Int): String? {
if (isAppUpdating(pkgInfo)) {
val existingUpdateOwnerLabel = getExistingUpdateOwnerLabel(pkgInfo)
- val requestedUpdateOwnerLabel = getApplicationLabel(callingPackage)
+
+ val originatingPackageNameFromSessionInfo =
+ getPackageNameForUid(context, originatingUidFromSessionInfo, callingPackage)
+ val requestedUpdateOwnerLabel =
+ getApplicationLabel(originatingPackageNameFromSessionInfo)
+
if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
&& userActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
index c9934adfad22..fb23637a9f4c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceModel.kt
@@ -115,7 +115,7 @@ internal class RestrictedSwitchPreferenceModel(
content: @Composable (SwitchPreferenceModel) -> Unit,
) {
val context = LocalContext.current
- val restrictedSwitchPreferenceModel = remember(restrictedMode, model.title) {
+ val restrictedSwitchPreferenceModel = remember(restrictedMode, model) {
RestrictedSwitchPreferenceModel(context, model, restrictedMode)
}
restrictedSwitchPreferenceModel.RestrictionWrapper {
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 32557b979bf5..a1587562d681 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -79,3 +79,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_determining_spatial_audio_attributes_by_profile"
+ namespace: "cross_device_experiences"
+ description: "Use bluetooth profile connection policy to determine spatial audio attributes"
+ bug: "341005211"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index b7758de0e19c..4e1d8e38dfbd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -671,7 +671,7 @@ public abstract class InfoMediaManager {
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
@VisibleForTesting
- void addMediaDevice(MediaRoute2Info route, RoutingSessionInfo activeSession) {
+ void addMediaDevice(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo activeSession) {
final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
@@ -711,8 +711,13 @@ public abstract class InfoMediaManager {
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
case TYPE_BLE_HEADSET:
+ if (route.getAddress() == null) {
+ Log.e(TAG, "Ignoring bluetooth route with no set address: " + route);
+ break;
+ }
final BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
+ BluetoothAdapter.getDefaultAdapter()
+ .getRemoteDevice(route.getAddress());
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 20b949f4a30f..8e072e632f3d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -20,6 +20,7 @@ import android.content.ContentResolver
import android.database.ContentObserver
import android.media.AudioDeviceInfo
import android.media.AudioManager
+import android.media.AudioManager.AudioDeviceCategory
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import android.provider.Settings
import androidx.concurrent.futures.DirectExecutor
@@ -85,6 +86,10 @@ interface AudioRepository {
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
+
+ /** Gets audio device category. */
+ @AudioDeviceCategory
+ suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
}
class AudioRepositoryImpl(
@@ -174,7 +179,7 @@ class AudioRepositoryImpl(
minVolume = getMinVolume(audioStream),
maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
volume = audioManager.getStreamVolume(audioStream.value),
- isAffectedByMute = audioManager.isStreamAffectedByMute(audioStream.value),
+ isAffectedByMute = audioManager.isStreamMutableByUi(audioStream.value),
isAffectedByRingerMode = audioManager.isStreamAffectedByRingerMode(audioStream.value),
isMuted = audioManager.isStreamMute(audioStream.value),
)
@@ -211,6 +216,13 @@ class AudioRepositoryImpl(
withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
}
+ @AudioDeviceCategory
+ override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int {
+ return withContext(backgroundCoroutineContext) {
+ audioManager.getBluetoothAudioDeviceCategory(bluetoothAddress)
+ }
+ }
+
private fun getMinVolume(stream: AudioStream): Int =
try {
audioManager.getStreamMinVolume(stream.value)
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
index 202ff400782f..bdd582d5130b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -61,6 +61,10 @@ class AudioVolumeInteractor(
}
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
+ val streamModel = getAudioStream(audioStream).first()
+ if (!streamModel.isAffectedByMute) {
+ return
+ }
if (audioStream.value == AudioManager.STREAM_RING) {
val mode =
if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 683759db95f9..844dc12ee911 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -247,6 +247,19 @@ class AudioRepositoryTest {
}
}
+ @Test
+ fun getBluetoothAudioDeviceCategory() {
+ testScope.runTest {
+ `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78")).thenReturn(
+ AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
+
+ val category = underTest.getBluetoothAudioDeviceCategory("12:34:56:78")
+ runCurrent()
+
+ assertThat(category).isEqualTo(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
+ }
+ }
+
private fun triggerConnectedDeviceChange(communicationDevice: AudioDeviceInfo?) {
verify(audioManager)
.addOnCommunicationDeviceChangedListener(
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 9f2ab69acd31..8b446b2db5d6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -134,6 +134,7 @@ public class SecureSettings {
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
+ Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index cb7ac457d991..2af1f4d136cf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -195,6 +195,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d54236e60dd7..00a7826aa8e8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -44,6 +44,7 @@ import static com.android.providers.settings.SettingsState.isSystemSettingsKey;
import static com.android.providers.settings.SettingsState.makeKey;
import android.Manifest;
+import android.aconfigd.AconfigdFlagInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -1369,10 +1370,27 @@ public class SettingsProvider extends ContentProvider {
}
}
+ Map<String, AconfigdFlagInfo> aconfigFlagInfos =
+ settingsState.getAconfigDefaultFlags();
+
for (int i = 0; i < nameCount; i++) {
String name = names.get(i);
Setting setting = settingsState.getSettingLocked(name);
- if (prefix == null || setting.getName().startsWith(prefix)) {
+ if (prefix == null || name.startsWith(prefix)) {
+ if (Flags.ignoreXmlForReadOnlyFlags()) {
+ int slashIndex = name.indexOf("/");
+ boolean validSlashIndex = slashIndex != -1
+ && slashIndex != 0
+ && slashIndex != name.length();
+ if (validSlashIndex) {
+ String flagName = name.substring(slashIndex + 1);
+ AconfigdFlagInfo flagInfo = aconfigFlagInfos.get(flagName);
+ if (flagInfo != null && !flagInfo.getIsReadWrite()) {
+ continue;
+ }
+ }
+ }
+
flagsToValues.put(setting.getName(), setting.getValue());
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 05eb0449495f..d3d30dfcea3a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -765,6 +765,13 @@ final class SettingsState {
}
}
+ @NonNull
+ public Map<String, AconfigdFlagInfo> getAconfigDefaultFlags() {
+ synchronized (mLock) {
+ return mAconfigDefaultFlags;
+ }
+ }
+
// The settings provider must hold its lock when calling here.
public Setting getSettingLocked(String name) {
if (TextUtils.isEmpty(name)) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index d20fbf591a25..4f5955b1c6ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -41,3 +41,14 @@ flag {
description: "If this flag is detected as true on boot, writes a logfile to track storage migration correctness."
bug: "328444881"
}
+
+flag {
+ name: "ignore_xml_for_read_only_flags"
+ namespace: "core_experiments_team_internal"
+ description: "When enabled, ignore any flag in the SettingsProvider XML for RO flags."
+ bug: "345007098"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b37db16f5786..666d939257dc 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -280,6 +280,9 @@
<!-- to adjust volume in volume panel -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <!-- to get bluetooth audio device category -->
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED" />
+
<!-- to access ResolverRankerServices -->
<uses-permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1cbf67ee0d0e..bfc36b84991a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1105,3 +1105,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "translucent_occluding_activity_fix"
+ namespace: "systemui"
+ description: "Fixes occlusion animation for transluent activities"
+ bug: "303010980"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index b6e4e9b13a1c..c14ee6208176 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -22,6 +22,7 @@ import android.app.PendingIntent
import android.app.TaskInfo
import android.app.WindowConfiguration
import android.content.ComponentName
+import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
@@ -53,6 +54,7 @@ import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import com.android.systemui.Flags.translucentOccludingActivityFix
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
import com.android.wm.shell.shared.IShellTransitions
import com.android.wm.shell.shared.ShellTransitions
@@ -991,7 +993,12 @@ constructor(
controller.createAnimatorState()
}
val windowBackgroundColor =
- window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor
+ if (translucentOccludingActivityFix() && window.isTranslucent) {
+ Color.TRANSPARENT
+ } else {
+ window.taskInfo?.let { callback.getBackgroundColor(it) }
+ ?: window.backgroundColor
+ }
// TODO(b/184121838): We should somehow get the top and bottom radius of the window
// instead of recomputing isExpandingFullyAbove here.
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index e39d7edddcfd..9e857deb280a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -21,16 +21,16 @@ import com.android.systemui.shared.settings.data.repository.SecureSettingsReposi
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Provides access to state related to notification settings. */
class NotificationSettingsRepository(
- scope: CoroutineScope,
+ private val scope: CoroutineScope,
private val backgroundDispatcher: CoroutineDispatcher,
private val secureSettingsRepository: SecureSettingsRepository,
) {
@@ -41,16 +41,15 @@ class NotificationSettingsRepository(
.distinctUntilChanged()
/** The current state of the notification setting. */
- val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
+ suspend fun isShowNotificationsOnLockScreenEnabled(): StateFlow<Boolean> =
secureSettingsRepository
.intSetting(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
)
.map { it == 1 }
+ .flowOn(backgroundDispatcher)
.stateIn(
scope = scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
)
suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
index 04e8090e3ae2..b4105bdecc0a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
@@ -26,8 +26,8 @@ class NotificationSettingsInteractor(
val isNotificationHistoryEnabled = repository.isNotificationHistoryEnabled
/** Should notifications be visible on the lockscreen? */
- val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
- repository.isShowNotificationsOnLockScreenEnabled
+ suspend fun isShowNotificationsOnLockScreenEnabled(): StateFlow<Boolean> =
+ repository.isShowNotificationsOnLockScreenEnabled()
suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
repository.setShowNotificationsOnLockscreenEnabled(enabled)
@@ -35,7 +35,7 @@ class NotificationSettingsInteractor(
/** Toggles the setting to show or hide notifications on the lock screen. */
suspend fun toggleShowNotificationsOnLockscreenEnabled() {
- val current = repository.isShowNotificationsOnLockScreenEnabled.value
+ val current = repository.isShowNotificationsOnLockScreenEnabled().value
repository.setShowNotificationsOnLockscreenEnabled(!current)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index fa79ea01cf51..54e0725957e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1284,6 +1284,41 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
+ public void onAodDownAndDownTouchReceived() throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+ -1 /* pointerId */, touchData);
+
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN fingerprint is requested because of AOD interrupt
+ // GIVEN there's been an AoD interrupt
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ mScreenObserver.onScreenTurnedOn();
+ mUdfpsController.onAodInterrupt(0, 0, 0, 0);
+ mFgExecutor.runAllReady();
+
+ // and an ACTION_DOWN is received and touch is within sensor
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
+ mBiometricExecutor.runAllReady();
+ firstDownEvent.recycle();
+
+ // THEN the touch is only processed once
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
+ anyBoolean());
+ }
+
+ @Test
public void onTouch_pilferPointerWhenAltBouncerShowing()
throws RemoteException {
final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 73ef77540398..88ba0411b414 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -84,7 +84,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
verify(mockAnimator, atLeastOnce()).addListener(captor.capture())
captor.allValues.forEach { it.onAnimationEnd(mockAnimator) }
- verify(stateController).setExitAnimationsRunning(false)
+ verify(stateController, times(2)).setExitAnimationsRunning(false)
}
@Test
@@ -154,4 +154,10 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
}
)
}
+
+ @Test
+ fun testCancelAnimations_clearsExitAnimationsRunning() {
+ controller.cancelAnimations()
+ verify(stateController).setExitAnimationsRunning(false)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 4587ea6dbdc8..c5ba02d0773a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -528,6 +528,30 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun userChange_isFingerprintEnrolledAndEnabledUpdated() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+ whenever(authController.isFingerprintEnrolled(ANOTHER_USER_ID)).thenReturn(false)
+ whenever(authController.isFingerprintEnrolled(PRIMARY_USER_ID)).thenReturn(true)
+
+ verify(biometricManager)
+ .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+ val isFingerprintEnrolledAndEnabled =
+ collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
+ biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
+ runCurrent()
+ userRepository.setSelectedUserInfo(ANOTHER_USER)
+ runCurrent()
+ assertThat(isFingerprintEnrolledAndEnabled()).isFalse()
+
+ biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
+ runCurrent()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ assertThat(isFingerprintEnrolledAndEnabled()).isTrue()
+ }
+
+ @Test
fun userChange_biometricEnabledChange_handlesRaceCondition() =
testScope.runTest {
createBiometricSettingsRepository()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index d20fec44d60f..5115f5af94f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -90,7 +90,11 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() {
)
reset(transitionRepository)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+ assertThat(transitionRepository).noTransitionsStarted()
+
kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
runCurrent()
kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 6d9c271d0f4a..b49e546c6e78 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -227,6 +227,15 @@ class DeviceEntryIconViewModelTest : SysuiTestCase() {
assertThat(accessibilityDelegateHint)
.isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
+ // udfps running
+ setUpState(
+ isUdfpsSupported = true,
+ isUdfpsRunning = true,
+ )
+
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
+
// non-interactive lock icon
fingerprintPropertyRepository.supportsRearFps()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
index d620639b2219..ef68e8a513dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -36,6 +36,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,6 +50,20 @@ class AudioVolumeInteractorTest : SysuiTestCase() {
private val underTest: AudioVolumeInteractor =
with(kosmos) { AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor) }
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ audioRepository.setAudioStreamModel(
+ audioRepository.getAudioStream(audioStream).value.copy(isAffectedByMute = true)
+ )
+ audioRepository.setAudioStreamModel(
+ audioRepository
+ .getAudioStream(AudioStream(AudioManager.STREAM_RING))
+ .value
+ .copy(isAffectedByMute = true)
+ )
+ }
+
@Test
fun setMuted_mutesStream() {
with(kosmos) {
@@ -194,10 +209,14 @@ class AudioVolumeInteractorTest : SysuiTestCase() {
testScope.runTest {
val audioStreamModel by collectLastValue(underTest.getAudioStream(audioStream))
audioRepository.setAudioStreamModel(
- audioStreamModel!!.copy(isAffectedByMute = false)
+ audioStreamModel!!.copy(isAffectedByMute = false, isMuted = false)
)
+ underTest.setMuted(audioStream, true)
+ runCurrent()
+
assertThat(audioStreamModel!!.isAffectedByMute).isFalse()
+ assertThat(audioStreamModel!!.isMuted).isFalse()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
index 777240c57c2e..5826b3feae37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.volume.panel.component.spatial
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
+import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
@@ -27,6 +29,8 @@ val Kosmos.spatialAudioComponentInteractor by
SpatialAudioComponentInteractor(
audioOutputInteractor,
spatializerInteractor,
+ audioRepository,
+ backgroundCoroutineContext,
testScope.backgroundScope
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
index 2f69942aa459..ebc78d864494 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.volume.panel.component.spatial.domain
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.session.MediaSession
@@ -24,12 +26,14 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerRepository
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.localMediaController
@@ -56,8 +60,15 @@ class SpatialAudioAvailabilityCriteriaTest : SysuiTestCase() {
@Before
fun setup() {
with(kosmos) {
+ val leAudioProfile =
+ mock<LeAudioProfile> {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(any())).thenReturn(true)
+ }
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(address).thenReturn("test_address")
+ whenever(profiles).thenReturn(listOf(leAudioProfile))
+ whenever(device).thenReturn(mock<BluetoothDevice> {})
}
localMediaRepository.updateCurrentConnectedDevice(
mock<BluetoothMediaDevice> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
index c6c46faf97f7..d5566adb1fdc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
@@ -16,14 +16,22 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.session.MediaSession
import android.media.session.PlaybackState
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.A2dpProfile
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -44,6 +52,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,15 +61,33 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class SpatialAudioComponentInteractorTest : SysuiTestCase() {
+ @get:Rule val setFlagsRule = SetFlagsRule()
private val kosmos = testKosmos()
private lateinit var underTest: SpatialAudioComponentInteractor
+ private val bluetoothDevice: BluetoothDevice = mock {}
+ private val a2dpProfile: A2dpProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.A2DP)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(false)
+ }
+ private val leAudioProfile: LeAudioProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(true)
+ }
+ private val hearingAidProfile: HearingAidProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.HEARING_AID)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(false)
+ }
+
@Before
fun setup() {
with(kosmos) {
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(address).thenReturn("test_address")
+ whenever(device).thenReturn(bluetoothDevice)
+ whenever(profiles)
+ .thenReturn(listOf(a2dpProfile, leAudioProfile, hearingAidProfile))
}
localMediaRepository.updateCurrentConnectedDevice(
mock<BluetoothMediaDevice> {
@@ -83,7 +110,7 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
fun setEnabled_changesIsEnabled() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
val values by collectValues(underTest.isEnabled)
underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
@@ -106,10 +133,39 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+ fun setEnabled_determinedByBluetoothProfile_a2dpProfileEnabled() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(a2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true)
+ whenever(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(false)
+ whenever(hearingAidProfile.isEnabled(bluetoothDevice)).thenReturn(false)
+ spatializerRepository.setIsSpatialAudioAvailable(a2dpAttributes, true)
+ val values by collectValues(underTest.isEnabled)
+
+ underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
+ runCurrent()
+ underTest.setEnabled(SpatialAudioEnabledModel.SpatialAudioEnabled)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ SpatialAudioEnabledModel.Unknown,
+ SpatialAudioEnabledModel.Disabled,
+ SpatialAudioEnabledModel.SpatialAudioEnabled,
+ )
+ .inOrder()
+ assertThat(spatializerRepository.getSpatialAudioCompatibleDevices())
+ .containsExactly(a2dpAttributes)
+ }
+ }
+ }
+
+ @Test
fun connectedDeviceSupports_isAvailable_SpatialAudio() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -123,8 +179,8 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
fun connectedDeviceSupportsHeadTracking_isAvailable_HeadTracking() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
- spatializerRepository.setIsHeadTrackingAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
+ spatializerRepository.setIsHeadTrackingAvailable(bleHeadsetAttributes, true)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -138,7 +194,7 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
fun connectedDeviceDoesntSupport_isAvailable_Unavailable() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, false)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, false)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -179,7 +235,13 @@ class SpatialAudioComponentInteractorTest : SysuiTestCase() {
}
private companion object {
- val headset =
+ val a2dpAttributes =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ "test_address"
+ )
+ val bleHeadsetAttributes =
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
index cf9ca157b943..c9850f2613b1 100644
--- a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -19,8 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/alternate_bouncer"
- android:focusable="true"
- android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/color/connected_network_primary_color.xml b/packages/SystemUI/res/color/connected_network_primary_color.xml
new file mode 100644
index 000000000000..f173c8dd5473
--- /dev/null
+++ b/packages/SystemUI/res/color/connected_network_primary_color.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/materialColorOnPrimaryContainer" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
index 250188b892f4..fab2d8db859f 100644
--- a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -16,10 +16,11 @@
-->
<ripple 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>
<shape android:shape="rectangle">
- <solid android:color="@color/settingslib_state_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
<corners android:radius="@dimen/settingslib_switch_bar_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
index 5566ea3f62fc..e316a93c1c3d 100644
--- a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item
android:top="@dimen/settingslib_switch_thumb_margin"
android:bottom="@dimen/settingslib_switch_thumb_margin">
@@ -23,7 +24,7 @@
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="@color/settingslib_state_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorOnPrimary"/>
</shape>
</item>
</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
index 1d9dacd6c0f9..e2e64684f9c3 100644
--- a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -16,11 +16,12 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle"
android:width="@dimen/settingslib_switch_track_width"
android:height="@dimen/settingslib_switch_track_height">
<padding android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"/>
- <solid android:color="@color/settingslib_track_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorPrimary"/>
<corners android:radius="@dimen/settingslib_switch_track_radius"/>
</shape>
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 84ab0f1b6ee5..fff1de7c1049 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -51,7 +51,6 @@
android:layout_marginBottom="@dimen/overlay_border_width"
android:layout_gravity="center"
android:elevation="4dp"
- android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/overlay_preview_background"
android:adjustViewBounds="true"
@@ -67,7 +66,6 @@
android:layout_marginBottom="@dimen/overlay_border_width"
android:layout_gravity="center"
android:elevation="4dp"
- android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/overlay_preview_background"
android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index fc4bf8a65643..e80ed26cffe5 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -22,5 +22,4 @@
android:layout_height="wrap_content"
app:lottie_autoPlay="true"
app:lottie_loop="true"
- app:lottie_rawRes="@raw/sfps_pulse"
- android:importantForAccessibility="no"/> \ No newline at end of file
+ app:lottie_rawRes="@raw/sfps_pulse"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 78fec55653b7..b1cc08661c73 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -181,6 +181,7 @@
<string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognized. Press to continue."</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognized. Press the unlock icon to continue."</string>
+ <string name="biometric_dialog_tap_confirm_with_face_sfps" msgid="2499213248903257928">"Unlocked by face. Tap to continue."</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Authenticated"</string>
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"Cancel Authentication"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"More Options"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 9a358dc016a2..8ef3602c21ec 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -181,6 +181,7 @@
<string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎Unlocked by face. Press to continue.‎‏‎‎‏‎"</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎Face recognized. Press to continue.‎‏‎‎‏‎"</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎Face recognized. Press the unlock icon to continue.‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_tap_confirm_with_face_sfps" msgid="2499213248903257928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎Unlocked by face. Tap to continue.‎‏‎‎‏‎"</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎Authenticated‎‏‎‎‏‎"</string>
<string name="biometric_dialog_cancel_authentication" msgid="981316588773442637">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎Cancel Authentication‎‏‎‎‏‎"</string>
<string name="biometric_dialog_content_view_more_options_button" msgid="2663810393874865475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎More Options‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index d377e0196e98..21f1cfbe4c9e 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -104,10 +104,6 @@
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
- <!-- 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-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 546bf1c3ac95..e9dd039f38c2 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -60,18 +60,6 @@
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
- <style name="TextAppearance.InternetDialog.Active">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/material_dynamic_primary80</item>
- <item name="android:textDirection">locale</item>
- </style>
-
- <style name="TextAppearance.InternetDialog.Secondary.Active">
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/material_dynamic_secondary80</item>
- </style>
-
<style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon">
<item name="android:windowLightNavigationBar">false</item>
</style>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ba59c2f99c52..5c62cce94c11 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -237,11 +237,8 @@
<!-- Internet Dialog -->
<!-- Material next state on color-->
<color name="settingslib_state_on_color">@color/settingslib_state_on</color>
- <!-- Material next track on color-->
- <color name="settingslib_track_on_color">@color/settingslib_track_on</color>
<!-- Material next track off color-->
<color name="settingslib_track_off_color">@color/settingslib_track_off</color>
- <color name="connected_network_primary_color">#191C18</color>
<color name="connected_network_secondary_color">#41493D</color>
<color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 838181252cb9..d51831b5730f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -441,6 +441,8 @@
<string name="biometric_dialog_tap_confirm_with_face_alt_2">Face recognized. Press to continue.</string>
<!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
<string name="biometric_dialog_tap_confirm_with_face_alt_3">Face recognized. Press the unlock icon to continue.</string>
+ <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication with SFPS [CHAR LIMIT=60]-->
+ <string name="biometric_dialog_tap_confirm_with_face_sfps">Unlocked by face. Tap to continue.</string>
<!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_authenticated">Authenticated</string>
<!-- Talkback string when a canceling authentication [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 73b7586f1210..7475eb2eceaa 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1315,7 +1315,7 @@
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
- <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+ <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
</style>
@@ -1358,6 +1358,7 @@
<style name="InternetDialog.NetworkTitle.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
</style>
<style name="InternetDialog.NetworkSummary">
@@ -1370,18 +1371,19 @@
<style name="InternetDialog.NetworkSummary.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
</style>
<style name="TextAppearance.InternetDialog">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
</style>
<style name="TextAppearance.InternetDialog.Active"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 5e76801aaca3..bf1f93f4693a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -28,6 +28,7 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -86,6 +87,12 @@ class ActiveUnlockConfig @Inject constructor(
* Trigger ActiveUnlock when the assistant is triggered.
*/
ASSISTANT,
+ /**
+ * Trigger ActiveUnlock on legacy unlock intents. This includes tapping on the empty space
+ * of the notification shadse when face auth is enrolled and re-trying face auth on the
+ * primary bouncer.
+ */
+ UNLOCK_INTENT_LEGACY,
}
/**
@@ -99,6 +106,7 @@ class ActiveUnlockConfig @Inject constructor(
}
private var requestActiveUnlockOnWakeup = false
+ private var requestActiveUnlockOnUnlockIntentLegacy = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
@@ -110,6 +118,8 @@ class ActiveUnlockConfig @Inject constructor(
private val settingsObserver = object : ContentObserver(handler) {
private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
+ private val unlockIntentLegacyUri =
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY)
private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
@@ -164,6 +174,15 @@ class ActiveUnlockConfig @Inject constructor(
ACTIVE_UNLOCK_ON_WAKE, 0, selectedUserInteractor.getSelectedUserId()) == 1
}
+ if (selfChange || uris.contains(unlockIntentLegacyUri)) {
+ requestActiveUnlockOnUnlockIntentLegacy =
+ secureSettings.getIntForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
+ 0,
+ selectedUserInteractor.getSelectedUserId()
+ ) == 1
+ }
+
if (selfChange || uris.contains(unlockIntentUri)) {
requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0,
@@ -257,7 +276,7 @@ class ActiveUnlockConfig @Inject constructor(
*/
fun isActiveUnlockEnabled(): Boolean {
return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnBioFail
+ requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntentLegacy
}
/**
@@ -299,15 +318,18 @@ class ActiveUnlockConfig @Inject constructor(
fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean {
return when (requestOrigin) {
ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup
-
+ ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY ->
+ requestActiveUnlockOnUnlockIntentLegacy
ActiveUnlockRequestOrigin.UNLOCK_INTENT ->
- requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
- (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
-
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnWakeup ||
+ (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
ActiveUnlockRequestOrigin.BIOMETRIC_FAIL ->
- requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnWakeup
-
+ requestActiveUnlockOnBioFail ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnWakeup
ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled()
}
}
@@ -345,6 +367,9 @@ class ActiveUnlockConfig @Inject constructor(
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Settings:")
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
+ pw.println(
+ " requestActiveUnlockOnUnlockIntentLegacy=$requestActiveUnlockOnUnlockIntentLegacy"
+ )
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 70465bc3d0b7..421782039043 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1097,7 +1097,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
AnimatorSet anims = new AnimatorSet();
- ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(mViewFlipper, View.TRANSLATION_Y,
+ yTranslation);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
0f);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 42838aeddd6b..428cd0e7da0a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -351,7 +351,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"swipeUpOnBouncer");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d6d40f28d288..b466f31cc509 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -995,6 +995,16 @@ public class AuthController implements
}
/**
+ * @return true if ultrasonic udfps HW is supported on this device. Can return true even if
+ * the user has not enrolled udfps. This may be false if called before
+ * onAllAuthenticatorsRegistered.
+ */
+ public boolean isUltrasonicUdfpsSupported() {
+ return getUdfpsProps() != null && !getUdfpsProps().isEmpty() && getUdfpsProps()
+ .get(0).isUltrasonicUdfps();
+ }
+
+ /**
* @return true if sfps HW is supported on this device. Can return true even if the user has
* not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ad142a86fa03..3dd375846499 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -270,6 +270,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@NonNull IUdfpsOverlayControllerCallback callback) {
+ mUdfpsOverlayInteractor.setRequestId(requestId);
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
new UdfpsControllerOverlay(
mContext,
@@ -404,6 +405,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {
handler::post,
authenticationCallback);
}
+
+ /**
+ * Debug to run setIgnoreDisplayTouches
+ */
+ public void debugSetIgnoreDisplayTouches(boolean ignoreTouch) {
+ final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
+ UdfpsController.this.mFingerprintManager.setIgnoreDisplayTouches(
+ requestId, mSensorProps.sensorId, ignoreTouch);
+ }
}
/**
@@ -558,7 +568,12 @@ public class UdfpsController implements DozeReceiver, Dumpable {
Log.w(TAG, "onTouch down received without a preceding up");
}
mActivePointerId = MotionEvent.INVALID_POINTER_ID;
- mOnFingerDown = false;
+
+ // It's possible on some devices to get duplicate touches from both doze and the
+ // normal touch listener. Don't reset the down in this case to avoid duplicate downs
+ if (!mIsAodInterruptActive) {
+ mOnFingerDown = false;
+ }
} else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
&& !mAlternateBouncerInteractor.isVisibleState())
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
index f5e3d29cb878..97ece11b8fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
@@ -75,6 +75,8 @@ class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command
simFingerUp()
} else if (args.size == 1 && args[0] == "biometricPrompt") {
launchBiometricPrompt()
+ } else if (args.size == 2 && args[0] == "setIgnoreDisplayTouches") {
+ setIgnoreDisplayTouches(args[1].toBoolean())
} else {
invalidCommand(pw)
}
@@ -186,6 +188,11 @@ class UdfpsShell @Inject constructor(commandRegistry: CommandRegistry) : Command
upEvent?.recycle()
}
+ @VisibleForTesting
+ fun setIgnoreDisplayTouches(ignoreTouches: Boolean) {
+ udfpsOverlayController?.debugSetIgnoreDisplayTouches(ignoreTouches)
+ }
+
private fun obtainMotionEvent(
action: Int,
x: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index a77cc1fea6a6..bb450c0b6d90 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
import android.util.Log
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthController
@@ -46,6 +47,7 @@ constructor(
@Application private val context: Context,
private val authController: AuthController,
private val selectedUserInteractor: SelectedUserInteractor,
+ private val fingerprintManager: FingerprintManager?,
@Application scope: CoroutineScope
) {
private fun calculateIconSize(): Int {
@@ -70,8 +72,25 @@ constructor(
return isUdfpsEnrolled && isWithinOverlayBounds
}
+ private var _requestId = MutableStateFlow(0L)
+
+ /** RequestId of current AcquisitionClient */
+ val requestId: StateFlow<Long> = _requestId.asStateFlow()
+
+ fun setRequestId(requestId: Long) {
+ _requestId.value = requestId
+ }
+
/** Sets whether Udfps overlay should handle touches */
fun setHandleTouches(shouldHandle: Boolean = true) {
+ if (authController.isUltrasonicUdfpsSupported
+ && shouldHandle != _shouldHandleTouches.value) {
+ fingerprintManager?.setIgnoreDisplayTouches(
+ requestId.value,
+ authController.udfpsProps!!.get(0).sensorId,
+ !shouldHandle
+ )
+ }
_shouldHandleTouches.value = shouldHandle
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 408e2c2d65a1..c868d01de743 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -580,17 +580,22 @@ class Spaghetti(
}
}
- private suspend fun getHelpForSuccessfulAuthentication(
+ private fun getHelpForSuccessfulAuthentication(
authenticatedModality: BiometricModality,
- ): Int? =
- when {
- // for coex, show a message when face succeeds after fingerprint has also started
- modalities.hasFaceAndFingerprint &&
- (viewModel.fingerprintStartMode.first() != FingerprintStartMode.Pending) &&
- (authenticatedModality == BiometricModality.Face) ->
- R.string.biometric_dialog_tap_confirm_with_face_alt_1
- else -> null
+ ): Int? {
+ // for coex, show a message when face succeeds after fingerprint has also started
+ if (authenticatedModality != BiometricModality.Face) {
+ return null
+ }
+
+ if (modalities.hasUdfps) {
+ return R.string.biometric_dialog_tap_confirm_with_face_alt_1
}
+ if (modalities.hasSfps) {
+ return R.string.biometric_dialog_tap_confirm_with_face_sfps
+ }
+ return null
+ }
fun onAuthenticationFailed(
@BiometricAuthenticator.Modality modality: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 9cc46506cefb..9578da4238ee 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -139,6 +139,11 @@ constructor(
overlayView!!.visibility = View.INVISIBLE
Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+ overlayView!!.announceForAccessibility(
+ applicationContext.resources.getString(
+ R.string.accessibility_side_fingerprint_indicator_label
+ )
+ )
}
/** Hide the side fingerprint sensor indicator */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 3294c816d67c..b45ebd865c55 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -256,6 +256,7 @@ constructor(
it.cancel()
null
}
+ mOverlayStateController.setExitAnimationsRunning(false)
}
private fun blurAnimator(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
index 297ad847caa6..befd822e14cd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
@@ -161,5 +161,6 @@ constructor(
TASK_FRAGMENT_TRANSIT_CLOSE,
false
)
+ organizer.unregisterOrganizer()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2d60fcca58cc..ed8d4263b212 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,6 +42,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.refactorGetCurrentUser;
+import static com.android.systemui.Flags.translucentOccludingActivityFix;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
import android.animation.Animator;
@@ -1035,6 +1036,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
(int) (fullWidth - initialWidth) /* left */,
fullWidth /* right */,
mWindowCornerRadius, mWindowCornerRadius);
+ } else if (translucentOccludingActivityFix()
+ && mOccludingRemoteAnimationTarget != null
+ && mOccludingRemoteAnimationTarget.isTranslucent) {
+ // Animating in a transparent window looks really weird. Just let it be
+ // fullscreen and the app can do an internal animation if it wants to.
+ return new TransitionAnimator.State(
+ 0,
+ fullHeight,
+ 0,
+ fullWidth,
+ 0f, 0f);
} else {
final float initialHeight = fullHeight / 2f;
final float initialWidth = fullWidth / 2f;
@@ -1398,6 +1410,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private final Lazy<DreamViewModel> mDreamViewModel;
private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel;
private RemoteAnimationTarget mRemoteAnimationTarget;
+
+ /**
+ * The most recent RemoteAnimationTarget provided for an occluding activity animation.
+ */
+ private RemoteAnimationTarget mOccludingRemoteAnimationTarget;
private boolean mShowCommunalWhenUnoccluding = false;
private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
@@ -3930,6 +3947,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ // Save mRemoteAnimationTarget for reference in the animation controller. Needs to be
+ // called prior to super.onAnimationStart() since that's the call that eventually asks
+ // the animation controller to configure the animation state.
+ if (apps.length > 0) {
+ mOccludingRemoteAnimationTarget = apps[0];
+ }
+
super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
mInteractionJankMonitor.begin(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 882f2315f6e8..dd3e6190ceaf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -188,35 +188,33 @@ constructor(
)
private val isFingerprintEnrolled: Flow<Boolean> =
- selectedUserId
- .flatMapLatest { currentUserId ->
- conflatedCallbackFlow {
- val callback =
- object : AuthController.Callback {
- override fun onEnrollmentsChanged(
- sensorBiometricType: BiometricType,
- userId: Int,
- hasEnrollments: Boolean
- ) {
- if (sensorBiometricType.isFingerprint && userId == currentUserId) {
- trySendWithFailureLogging(
- hasEnrollments,
- TAG,
- "update fpEnrollment"
- )
- }
+ selectedUserId.flatMapLatest { currentUserId ->
+ conflatedCallbackFlow {
+ val callback =
+ object : AuthController.Callback {
+ override fun onEnrollmentsChanged(
+ sensorBiometricType: BiometricType,
+ userId: Int,
+ hasEnrollments: Boolean
+ ) {
+ if (sensorBiometricType.isFingerprint && userId == currentUserId) {
+ trySendWithFailureLogging(
+ hasEnrollments,
+ TAG,
+ "update fpEnrollment"
+ )
}
}
- authController.addCallback(callback)
- awaitClose { authController.removeCallback(callback) }
- }
+ }
+ authController.addCallback(callback)
+ trySendWithFailureLogging(
+ authController.isFingerprintEnrolled(currentUserId),
+ TAG,
+ "Initial value of fingerprint enrollment"
+ )
+ awaitClose { authController.removeCallback(callback) }
}
- .stateIn(
- scope,
- started = SharingStarted.Eagerly,
- initialValue =
- authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id)
- )
+ }
private val isFaceEnrolled: Flow<Boolean> =
selectedUserId.flatMapLatest { selectedUserId: Int ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 49d00af61a24..5573f0d5f644 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -40,6 +40,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
@@ -168,7 +169,9 @@ constructor(
keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit
keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded ->
if (keyguardOccluded) {
- primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled
+ primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled.drop(
+ 1
+ ) // drop the initial state
} else {
emptyFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 6550937d2db0..f8063c92124d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -86,7 +86,10 @@ constructor(
privateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ // Avoid announcing window title.
+ accessibilityTitle = " "
}
+
private var alternateBouncerView: ConstraintLayout? = null
override fun start() {
@@ -304,6 +307,7 @@ constructor(
}
}
}
+
companion object {
private const val TAG = "AlternateBouncerViewBinder"
private const val swipeTag = "AlternateBouncer-SWIPE"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 215ac4662b55..1c6323594c70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -73,14 +73,14 @@ constructor(
AccessibilityNodeInfoCompat.ACTION_CLICK,
resources.getString(R.string.accessibility_enter_hint)
)
+
override fun onInitializeAccessibilityNodeInfo(
v: View,
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(v, info)
when (accessibilityHintType) {
- AccessibilityHintType.BOUNCER ->
- info.addAction(accessibilityBouncerHint)
+ AccessibilityHintType.BOUNCER -> info.addAction(accessibilityBouncerHint)
AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
AccessibilityHintType.NONE -> return
}
@@ -204,12 +204,12 @@ constructor(
/* reversible */ false,
)
- // LockscreenFingerprint <=> LockscreenLocked
+ // LockscreenFingerprint => LockscreenLocked
animatedIconDrawable.addTransition(
R.id.locked_fp,
R.id.locked,
context.getDrawable(R.drawable.fp_to_locked) as AnimatedVectorDrawable,
- /* reversible */ true,
+ /* reversible */ false,
)
// LockscreenUnlocked <=> AodLocked
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 4128c529644d..5cf100e78e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -34,8 +34,8 @@ constructor(
alternateBouncerInteractor: AlternateBouncerInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
- val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer
-
+ private val deviceSupportsAlternateBouncer: Flow<Boolean> =
+ alternateBouncerInteractor.alternateBouncerSupported
private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
keyguardTransitionInteractor
.transitionValue(KeyguardState.ALTERNATE_BOUNCER)
@@ -43,8 +43,8 @@ constructor(
.distinctUntilChanged()
val alternateBouncerWindowRequired: Flow<Boolean> =
- canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer ->
- if (canShowAlternateBouncer) {
+ deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
+ if (deviceSupportsAlternateBouncer) {
isTransitioningToOrFromOrShowingAlternateBouncer
} else {
flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 46880883ac70..5ce1b5e3dcc5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -284,9 +284,9 @@ constructor(
private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
DeviceEntryIconView.AccessibilityHintType {
return when (this) {
+ DeviceEntryIconView.IconType.FINGERPRINT,
DeviceEntryIconView.IconType.LOCK -> DeviceEntryIconView.AccessibilityHintType.BOUNCER
DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
- DeviceEntryIconView.IconType.FINGERPRINT,
DeviceEntryIconView.IconType.NONE -> DeviceEntryIconView.AccessibilityHintType.NONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
new file mode 100644
index 000000000000..6730d2d86d5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.screenshot
+
+import android.net.Uri
+import android.os.UserManager
+import android.util.Log
+import android.view.WindowManager
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.UUID
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * A ScreenshotHandler that just saves the screenshot and calls back as appropriate, with no UI.
+ *
+ * Basically, ScreenshotController with all the UI bits ripped out.
+ */
+class HeadlessScreenshotHandler
+@Inject
+constructor(
+ private val imageExporter: ImageExporter,
+ @Main private val mainExecutor: Executor,
+ private val imageCapture: ImageCapture,
+ private val userManager: UserManager,
+ private val uiEventLogger: UiEventLogger,
+ private val notificationsControllerFactory: ScreenshotNotificationsController.Factory,
+) : ScreenshotHandler {
+
+ override fun handleScreenshot(
+ screenshot: ScreenshotData,
+ finisher: Consumer<Uri?>,
+ requestCallback: TakeScreenshotService.RequestCallback
+ ) {
+ if (screenshot.type == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
+ screenshot.bitmap = imageCapture.captureDisplay(screenshot.displayId, crop = null)
+ }
+
+ if (screenshot.bitmap == null) {
+ Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
+ notificationsControllerFactory
+ .create(screenshot.displayId)
+ .notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
+ requestCallback.reportError()
+ return
+ }
+
+ val future: ListenableFuture<ImageExporter.Result> =
+ imageExporter.export(
+ Executors.newSingleThreadExecutor(),
+ UUID.randomUUID(),
+ screenshot.bitmap,
+ screenshot.getUserOrDefault(),
+ screenshot.displayId
+ )
+ future.addListener(
+ {
+ try {
+ val result = future.get()
+ Log.d(TAG, "Saved screenshot: $result")
+ logScreenshotResultStatus(result.uri, screenshot)
+ finisher.accept(result.uri)
+ requestCallback.onFinish()
+ } catch (e: Exception) {
+ Log.d(TAG, "Failed to store screenshot", e)
+ finisher.accept(null)
+ requestCallback.reportError()
+ }
+ },
+ mainExecutor
+ )
+ }
+
+ private fun logScreenshotResultStatus(uri: Uri?, screenshot: ScreenshotData) {
+ if (uri == null) {
+ uiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, screenshot.packageNameString)
+ notificationsControllerFactory
+ .create(screenshot.displayId)
+ .notifyScreenshotError(R.string.screenshot_failed_to_save_text)
+ } else {
+ uiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, screenshot.packageNameString)
+ if (userManager.isManagedProfile(screenshot.getUserOrDefault().identifier)) {
+ uiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE,
+ 0,
+ screenshot.packageNameString
+ )
+ }
+ }
+ }
+
+ companion object {
+ const val TAG = "HeadlessScreenshotHandler"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
index 2ffb7835f400..5f1688650cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
@@ -18,6 +18,7 @@ package com.android.systemui.screenshot
import android.app.assist.AssistContent
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -84,9 +85,9 @@ constructor(
}
inner class ActionsCallback(private val screenshotId: UUID) {
- fun providePreviewAction(onClick: () -> Unit) {
+ fun providePreviewAction(previewAction: PreviewAction) {
if (screenshotId == currentScreenshotId) {
- viewModel.setPreviewAction(onClick)
+ viewModel.setPreviewAction(previewAction)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index b8029c8b1cc3..c216f1da2dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -29,6 +29,7 @@ import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -40,7 +41,9 @@ import java.util.UUID
*/
interface ScreenshotActionsProvider {
fun onScrollChipReady(onClick: Runnable)
+
fun onScrollChipInvalidated()
+
fun setCompletedScreenshot(result: ScreenshotSavedResult)
/**
@@ -75,17 +78,19 @@ constructor(
private var result: ScreenshotSavedResult? = null
init {
- actionsCallback.providePreviewAction {
- debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
- uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
- onDeferrableActionTapped { result ->
- actionExecutor.startSharedTransition(
- createEdit(result.uri, context),
- result.user,
- true
- )
+ actionsCallback.providePreviewAction(
+ PreviewAction(context.resources.getString(R.string.screenshot_edit_description)) {
+ debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
+ uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
+ onDeferrableActionTapped { result ->
+ actionExecutor.startSharedTransition(
+ createEdit(result.uri, context),
+ result.user,
+ true
+ )
+ }
}
- }
+ )
actionsCallback.provideActionButton(
ActionButtonAppearance(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index e8dfac868546..c87b1f526957 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -101,7 +101,7 @@ import javax.inject.Provider;
/**
* Controls the state and flow for screenshots.
*/
-public class ScreenshotController {
+public class ScreenshotController implements ScreenshotHandler {
private static final String TAG = logTag(ScreenshotController.class);
/**
@@ -351,7 +351,8 @@ public class ScreenshotController {
mShowUIOnExternalDisplay = showUIOnExternalDisplay;
}
- void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
+ @Override
+ public void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
RequestCallback requestCallback) {
Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 40d709d0ab25..6a9615081d72 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.screenshot
import android.net.Uri
@@ -12,6 +28,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
import java.util.function.Consumer
import javax.inject.Inject
@@ -25,9 +42,13 @@ interface TakeScreenshotExecutor {
onSaved: (Uri?) -> Unit,
requestCallback: RequestCallback
)
+
fun onCloseSystemDialogsReceived()
+
fun removeWindows()
+
fun onDestroy()
+
fun executeScreenshotsAsync(
screenshotRequest: ScreenshotRequest,
onSaved: Consumer<Uri?>,
@@ -35,6 +56,14 @@ interface TakeScreenshotExecutor {
)
}
+interface ScreenshotHandler {
+ fun handleScreenshot(
+ screenshot: ScreenshotData,
+ finisher: Consumer<Uri?>,
+ requestCallback: RequestCallback
+ )
+}
+
/**
* Receives the signal to take a screenshot from [TakeScreenshotService], and calls back with the
* result.
@@ -51,10 +80,10 @@ constructor(
private val screenshotRequestProcessor: ScreenshotRequestProcessor,
private val uiEventLogger: UiEventLogger,
private val screenshotNotificationControllerFactory: ScreenshotNotificationsController.Factory,
+ private val headlessScreenshotHandler: HeadlessScreenshotHandler,
) : TakeScreenshotExecutor {
-
private val displays = displayRepository.displays
- private val screenshotControllers = mutableMapOf<Int, ScreenshotController>()
+ private var screenshotController: ScreenshotController? = null
private val notificationControllers = mutableMapOf<Int, ScreenshotNotificationsController>()
/**
@@ -72,9 +101,15 @@ constructor(
val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
displays.forEach { display ->
val displayId = display.displayId
+ var screenshotHandler: ScreenshotHandler =
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ getScreenshotController(display)
+ } else {
+ headlessScreenshotHandler
+ }
Log.d(TAG, "Executing screenshot for display $displayId")
dispatchToController(
- display = display,
+ screenshotHandler,
rawScreenshotData = ScreenshotData.fromRequest(screenshotRequest, displayId),
onSaved =
if (displayId == Display.DEFAULT_DISPLAY) {
@@ -87,7 +122,7 @@ constructor(
/** All logging should be triggered only by this method. */
private suspend fun dispatchToController(
- display: Display,
+ screenshotHandler: ScreenshotHandler,
rawScreenshotData: ScreenshotData,
onSaved: (Uri?) -> Unit,
callback: RequestCallback
@@ -101,13 +136,12 @@ constructor(
logScreenshotRequested(rawScreenshotData)
onFailedScreenshotRequest(rawScreenshotData, callback)
}
- .getOrNull()
- ?: return
+ .getOrNull() ?: return
logScreenshotRequested(screenshotData)
Log.d(TAG, "Screenshot request: $screenshotData")
try {
- getScreenshotController(display).handleScreenshot(screenshotData, onSaved, callback)
+ screenshotHandler.handleScreenshot(screenshotData, onSaved, callback)
} catch (e: IllegalStateException) {
Log.e(TAG, "Error while ScreenshotController was handling ScreenshotData!", e)
onFailedScreenshotRequest(screenshotData, callback)
@@ -147,36 +181,24 @@ constructor(
}
}
- /** Propagates the close system dialog signal to all controllers. */
+ /** Propagates the close system dialog signal to the ScreenshotController. */
override fun onCloseSystemDialogsReceived() {
- screenshotControllers.forEach { (_, screenshotController) ->
- if (!screenshotController.isPendingSharedTransition) {
- screenshotController.requestDismissal(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
- }
+ if (screenshotController?.isPendingSharedTransition == false) {
+ screenshotController?.requestDismissal(SCREENSHOT_DISMISSED_OTHER)
}
}
/** Removes all screenshot related windows. */
override fun removeWindows() {
- screenshotControllers.forEach { (_, screenshotController) ->
- screenshotController.removeWindow()
- }
+ screenshotController?.removeWindow()
}
/**
* Destroys the executor. Afterwards, this class is not expected to work as intended anymore.
*/
override fun onDestroy() {
- screenshotControllers.forEach { (_, screenshotController) ->
- screenshotController.onDestroy()
- }
- screenshotControllers.clear()
- }
-
- private fun getScreenshotController(display: Display): ScreenshotController {
- return screenshotControllers.computeIfAbsent(display.displayId) {
- screenshotControllerFactory.create(display, /* showUIOnExternalDisplay= */ false)
- }
+ screenshotController?.onDestroy()
+ screenshotController = null
}
private fun getNotificationController(id: Int): ScreenshotNotificationsController {
@@ -196,6 +218,12 @@ constructor(
}
}
+ private fun getScreenshotController(display: Display): ScreenshotController {
+ val controller = screenshotController ?: screenshotControllerFactory.create(display, false)
+ screenshotController = controller
+ return controller
+ }
+
/**
* Returns a [RequestCallback] that wraps [originalCallback].
*
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
index 4f27b9e4dbf0..b3d5c9e9691c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
@@ -99,7 +99,7 @@ class PolicyRequestProcessor(
original,
updates.component,
updates.owner,
- type.displayId
+ type.displayId,
)
}
return updated
@@ -120,6 +120,7 @@ class PolicyRequestProcessor(
return replaceWithScreenshot(
original = original,
componentName = topMainRootTask?.topActivity ?: defaultComponent,
+ taskId = topMainRootTask?.taskId,
owner = defaultOwner,
displayId = original.displayId
)
@@ -144,11 +145,12 @@ class PolicyRequestProcessor(
)
}
- suspend fun replaceWithScreenshot(
+ private suspend fun replaceWithScreenshot(
original: ScreenshotData,
componentName: ComponentName?,
owner: UserHandle?,
displayId: Int,
+ taskId: Int? = null,
): ScreenshotData {
Log.i(TAG, "Capturing screenshot: $componentName / $owner")
val screenshot = captureDisplay(displayId)
@@ -157,7 +159,8 @@ class PolicyRequestProcessor(
bitmap = screenshot,
userHandle = owner,
topComponent = componentName,
- screenBounds = Rect(0, 0, screenshot?.width ?: 0, screenshot?.height ?: 0)
+ screenBounds = Rect(0, 0, screenshot?.width ?: 0, screenshot?.height ?: 0),
+ taskId = taskId ?: -1,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 442b3873be4d..0fefa0b7757a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -128,8 +128,9 @@ constructor(private val buttonViewBinder: ActionButtonViewBinder) {
}
}
launch {
- viewModel.previewAction.collect { onClick ->
- previewView.setOnClickListener { onClick?.invoke() }
+ viewModel.previewAction.collect { action ->
+ previewView.setOnClickListener { action?.onClick?.invoke() }
+ previewView.contentDescription = action?.contentDescription
}
}
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 3f99bc4597cb..25420d44dc79 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -31,8 +31,8 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager
val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim
private val _badge = MutableStateFlow<Drawable?>(null)
val badge: StateFlow<Drawable?> = _badge
- private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
- val previewAction: StateFlow<(() -> Unit)?> = _previewAction
+ private val _previewAction = MutableStateFlow<PreviewAction?>(null)
+ val previewAction: StateFlow<PreviewAction?> = _previewAction
private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
val actions: StateFlow<List<ActionButtonViewModel>> = _actions
private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED)
@@ -57,8 +57,8 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager
_badge.value = badge
}
- fun setPreviewAction(onClick: () -> Unit) {
- _previewAction.value = onClick
+ fun setPreviewAction(previewAction: PreviewAction) {
+ _previewAction.value = previewAction
}
fun addAction(
@@ -149,6 +149,11 @@ class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager
}
}
+data class PreviewAction(
+ val contentDescription: CharSequence,
+ val onClick: () -> Unit,
+)
+
enum class AnimationState {
NOT_STARTED,
ENTRANCE_STARTED, // The first 200ms of the entrance animation
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 262befc1e9ad..fff5e104a7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1770,8 +1770,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// the small clock here
// With migrateClocksToBlueprint, weather clock will have behaviors similar to other clocks
if (!MigrateClocksToBlueprint.isEnabled()) {
+ boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
- && hasVisibleNotifications() && isOnAod()) {
+ && hasVisibleNotifications() && (isOnAod() || bypassEnabled)) {
return SMALL;
}
}
@@ -3046,7 +3047,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"lockScreenEmptySpaceTap");
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
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 4b1ee58c2cc6..02187844d6da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -68,7 +68,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
- private boolean mIsIdleOnGone = false;
+ private boolean mIsIdleOnGone = true;
private boolean mShouldAdjustInsets = false;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
@@ -282,7 +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
- || !mIsIdleOnGone
+ || (SceneContainerFlag.isEnabled() && !mIsIdleOnGone)
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index ec3af87f2b9b..a7c5f78e5b69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -345,32 +345,67 @@ constructor(
orElse = flowOf(false),
retrySignal = telephonyProcessCrashedEvent,
)
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ .stateIn(scope, SharingStarted.Eagerly, false)
private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
conflatedCallbackFlow {
- val callback = SatelliteProvisionStateCallback { provisioned ->
- logBuffer.i {
- "onSatelliteProvisionStateChanged: " +
- if (provisioned) "provisioned" else "not provisioned"
+ // TODO(b/347992038): SatelliteManager should be sending the current provisioned
+ // status when we register a callback. Until then, we have to manually query here.
+
+ // First, check to see what the current status is, and send the result to the output
+ trySend(queryIsSatelliteProvisioned(sm))
+
+ val callback = SatelliteProvisionStateCallback { provisioned ->
+ logBuffer.i {
+ "onSatelliteProvisionStateChanged: " +
+ if (provisioned) "provisioned" else "not provisioned"
+ }
+ trySend(provisioned)
}
- trySend(provisioned)
- }
- var registered = false
- try {
- sm.registerForProvisionStateChanged(
- bgDispatcher.asExecutor(),
- callback,
- )
- registered = true
- } catch (e: Exception) {
- logBuffer.e("error registering for provisioning state callback", e)
+ var registered = false
+ try {
+ logBuffer.i { "registerForProvisionStateChanged" }
+ sm.registerForProvisionStateChanged(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+ registered = true
+ } catch (e: Exception) {
+ logBuffer.e("error registering for provisioning state callback", e)
+ }
+
+ awaitClose {
+ if (registered) {
+ sm.unregisterForProvisionStateChanged(callback)
+ }
+ }
}
+ .flowOn(bgDispatcher)
+
+ /** Check the current satellite provisioning status. */
+ private suspend fun queryIsSatelliteProvisioned(sm: SupportedSatelliteManager): Boolean =
+ withContext(bgDispatcher) {
+ suspendCancellableCoroutine { continuation ->
+ val receiver =
+ object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+ override fun onResult(result: Boolean) {
+ logBuffer.i { "requestIsProvisioned.onResult: $result" }
+ continuation.resume(result)
+ }
- awaitClose {
- if (registered) {
- sm.unregisterForProvisionStateChanged(callback)
+ override fun onError(exception: SatelliteManager.SatelliteException) {
+ logBuffer.e("requestIsProvisioned.onError:", exception)
+ continuation.resume(false)
+ }
+ }
+
+ logBuffer.i { "Query for current satellite provisioned state." }
+ try {
+ sm.requestIsProvisioned(bgDispatcher.asExecutor(), receiver)
+ } catch (e: Exception) {
+ logBuffer.e("Exception while calling SatelliteManager.requestIsProvisioned:", e)
+ continuation.resume(false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index aee441a13a5d..c45f98e5f4f5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -567,7 +567,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
updateStreamMuteW(stream, mAudio.isStreamMute(stream));
final StreamState ss = streamStateW(stream);
- ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
+ ss.muteSupported = mAudio.isStreamMutableByUi(stream);
ss.name = STREAMS.get(stream);
checkRoutedToBluetoothW(stream);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index 7f1faeeb381d..cfcd6b14010d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -16,15 +16,23 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
+import android.media.AudioManager
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothProfile
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -33,6 +41,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/**
* Provides an ability to access and update spatial audio and head tracking state.
@@ -46,6 +55,8 @@ class SpatialAudioComponentInteractor
constructor(
audioOutputInteractor: AudioOutputInteractor,
private val spatializerInteractor: SpatializerInteractor,
+ private val audioRepository: AudioRepository,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
@VolumePanelScope private val coroutineScope: CoroutineScope,
) {
@@ -138,42 +149,85 @@ constructor(
}
private suspend fun AudioOutputDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
- when (this) {
- is AudioOutputDevice.BuiltIn -> return builtinSpeaker
+ return when (this) {
+ is AudioOutputDevice.BuiltIn -> builtinSpeaker
is AudioOutputDevice.Bluetooth -> {
- return listOf(
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_BROADCAST,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_HEARING_AID,
- cachedBluetoothDevice.address,
+ if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) {
+ getAudioDeviceAttributesByBluetoothProfile(cachedBluetoothDevice)
+ } else {
+ listOf(
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ cachedBluetoothDevice.address,
+ )
)
- )
- .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
+ .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
+ }
}
- else -> return null
+ else -> null
}
}
+ private suspend fun getAudioDeviceAttributesByBluetoothProfile(
+ cachedBluetoothDevice: CachedBluetoothDevice
+ ): AudioDeviceAttributes? =
+ withContext(backgroundCoroutineContext) {
+ cachedBluetoothDevice.profiles
+ .firstOrNull {
+ it.profileId in audioProfiles && it.isEnabled(cachedBluetoothDevice.device)
+ }
+ ?.let { profile: LocalBluetoothProfile ->
+ when (profile.profileId) {
+ BluetoothProfile.A2DP -> {
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
+ }
+ BluetoothProfile.LE_AUDIO -> {
+ when (
+ audioRepository.getBluetoothAudioDeviceCategory(
+ cachedBluetoothDevice.address
+ )
+ ) {
+ AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER ->
+ AudioDeviceInfo.TYPE_BLE_SPEAKER
+ else -> AudioDeviceInfo.TYPE_BLE_HEADSET
+ }
+ }
+ BluetoothProfile.HEARING_AID -> {
+ AudioDeviceInfo.TYPE_HEARING_AID
+ }
+ else -> null
+ }
+ }
+ ?.let {
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ it,
+ cachedBluetoothDevice.address,
+ )
+ }
+ }
+
private companion object {
val builtinSpeaker =
AudioDeviceAttributes(
@@ -181,5 +235,7 @@ constructor(
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
""
)
+ val audioProfiles =
+ setOf(BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 8858d132c4be..48f6cc4261a9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -28,6 +28,7 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -40,6 +41,7 @@ import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import dagger.Lazy
import java.io.PrintWriter
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -51,7 +53,6 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import dagger.Lazy
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -111,6 +112,48 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
+ }
+
+ @Test
+ fun onUnlockIntentLegacySettingChanged() {
+ // GIVEN no active unlock settings enabled
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+
+ // WHEN unlock on unlock intent legacy is allowed
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY))
+
+ // THEN active unlock triggers allowed on unlock_intent_legacy, unlock_intent,
+ // AND biometric fail
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -132,16 +175,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
)
)
- // WHEN unlock on biometric failed is allowed
+ // WHEN unlock on unlock intent is allowed
secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
- // THEN active unlock triggers allowed on: biometric failure ONLY
+ // THEN active unlock triggers allowed on: unlock intent AND biometric failure
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -184,6 +232,11 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
)
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
)
)
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 9a99ed7bb512..1e3ee280204b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -27,6 +27,7 @@ import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
import android.os.IBinder
@@ -88,9 +89,8 @@ import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
-
+import org.mockito.junit.MockitoJUnit
private const val OP_PACKAGE_NAME = "biometric.testapp"
@@ -99,33 +99,21 @@ private const val OP_PACKAGE_NAME = "biometric.testapp"
@SmallTest
open class AuthContainerViewTest : SysuiTestCase() {
- @JvmField @Rule
- var mockitoRule = MockitoJUnit.rule()
-
- @Mock
- lateinit var callback: AuthDialogCallback
- @Mock
- lateinit var userManager: UserManager
- @Mock
- lateinit var lockPatternUtils: LockPatternUtils
- @Mock
- lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock
- lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
- @Mock
- lateinit var windowToken: IBinder
- @Mock
- lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock
- lateinit var vibrator: VibratorHelper
- @Mock
- lateinit var udfpsUtils: UdfpsUtils
- @Mock
- lateinit var authController: AuthController
- @Mock
- lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock
- private lateinit var packageManager: PackageManager
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ @Mock lateinit var callback: AuthDialogCallback
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var fingerprintManager: FingerprintManager
+ @Mock lateinit var lockPatternUtils: LockPatternUtils
+ @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
+ @Mock lateinit var windowToken: IBinder
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var vibrator: VibratorHelper
+ @Mock lateinit var udfpsUtils: UdfpsUtils
+ @Mock lateinit var authController: AuthController
+ @Mock lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var activityTaskManager: ActivityTaskManager
private lateinit var displayRepository: FakeDisplayRepository
@@ -141,11 +129,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val displayStateRepository = FakeDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
- private val bpCredentialInteractor = PromptCredentialInteractor(
- Dispatchers.Main.immediate,
- biometricPromptRepository,
- credentialInteractor,
- )
+ private val bpCredentialInteractor =
+ PromptCredentialInteractor(
+ Dispatchers.Main.immediate,
+ biometricPromptRepository,
+ credentialInteractor,
+ )
private val promptSelectorInteractor by lazy {
PromptSelectorInteractorImpl(
fingerprintRepository,
@@ -166,22 +155,26 @@ open class AuthContainerViewTest : SysuiTestCase() {
displayStateInteractor =
DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ displayStateRepository,
+ displayRepository,
)
udfpsOverlayInteractor =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- testScope.backgroundScope,
- )
+ UdfpsOverlayInteractor(
+ context,
+ authController,
+ selectedUserInteractor,
+ fingerprintManager,
+ testScope.backgroundScope,
+ )
biometricStatusInteractor =
- BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository,
- fingerprintRepository)
+ BiometricStatusInteractorImpl(
+ activityTaskManager,
+ biometricStatusRepository,
+ fingerprintRepository
+ )
iconProvider = IconProvider(context)
// Set up default logo icon
whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
@@ -198,10 +191,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testNotifiesAnimatedIn() {
initializeFingerprintContainer()
- verify(callback).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback)
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -246,10 +237,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
// attaching the view resets the state and allows this to happen again
- verify(callback).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback)
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -274,10 +263,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
// the first time is triggered by initializeFingerprintContainer()
// the second time was triggered by dismissWithoutCallback()
- verify(callback, times(2)).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback, times(2))
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -288,18 +275,18 @@ open class AuthContainerViewTest : SysuiTestCase() {
verify(panelInteractionDetector).disable()
}
-
@Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
container.mBiometricCallback.onAuthenticated()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@@ -309,15 +296,17 @@ open class AuthContainerViewTest : SysuiTestCase() {
container.mBiometricCallback.onUserCanceled()
waitForIdleSync()
- verify(callback).onSystemEvent(
+ verify(callback)
+ .onSystemEvent(
eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL),
eq(authContainer?.requestId ?: 0L)
- )
- verify(callback).onDismissed(
+ )
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@@ -327,19 +316,21 @@ open class AuthContainerViewTest : SysuiTestCase() {
container.mBiometricCallback.onButtonNegative()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@Test
fun testActionTryAgain_sendsTryAgain() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+ )
container.mBiometricCallback.onButtonTryAgain()
waitForIdleSync()
@@ -352,21 +343,24 @@ open class AuthContainerViewTest : SysuiTestCase() {
container.mBiometricCallback.onError()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_ERROR),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(authContainer!!.parent).isNull()
}
@Ignore("b/279650412")
@Test
fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.mBiometricCallback.onUseDeviceCredential()
waitForIdleSync()
@@ -376,10 +370,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.animateToCredentialUI(false)
waitForIdleSync()
@@ -395,10 +391,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testAnimateToCredentialUI_rotateCredentialUI() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.animateToCredentialUI(false)
waitForIdleSync()
@@ -437,13 +435,12 @@ open class AuthContainerViewTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
var isButtonClicked = false
val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(
- fakeExecutor) { _, _ -> isButtonClicked = true }
- .build()
+ PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> isButtonClicked = true }
+ .build()
val container =
- initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView)
+ initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView)
waitForIdleSync()
@@ -461,9 +458,9 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowCredentialUI_withDescription() {
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
@@ -475,10 +472,10 @@ open class AuthContainerViewTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
- verticalListContentView = PromptVerticalListContentView.Builder().build()
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ verticalListContentView = PromptVerticalListContentView.Builder().build()
+ )
// Two-step credential view should show -
// 1. biometric prompt without sensor 2. credential view ui
waitForIdleSync()
@@ -497,14 +494,14 @@ open class AuthContainerViewTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
- .build()
+ PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+ .build()
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
- contentViewWithMoreOptionsButton = contentView
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ contentViewWithMoreOptionsButton = contentView
+ )
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
@@ -514,13 +511,13 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testCredentialViewUsesEffectiveUserId() {
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
- )
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200)))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialPatternView()).isTrue()
@@ -531,9 +528,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
fun testCredentialUI_disablesClickingOnBackground() {
val container = initializeCredentialPasswordContainer()
assertThat(container.hasBiometricPrompt()).isFalse()
- assertThat(
- container.findViewById<View>(R.id.background)?.isImportantForAccessibility
- ).isFalse()
+ assertThat(container.findViewById<View>(R.id.background)?.isImportantForAccessibility)
+ .isFalse()
container.findViewById<View>(R.id.background)?.performClick()
waitForIdleSync()
@@ -552,7 +548,7 @@ open class AuthContainerViewTest : SysuiTestCase() {
fun testLayoutParams_hasShowWhenLockedFlag() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.flags and WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) != 0)
- .isTrue()
+ .isTrue()
}
@Test
@@ -590,20 +586,20 @@ open class AuthContainerViewTest : SysuiTestCase() {
}
private fun initializeCredentialPasswordContainer(
- addToView: Boolean = true,
+ addToView: Boolean = true,
): TestAuthContainerView {
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- )
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20)))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)
// In the credential view, clicking on the background (to cancel authentication) is not
// valid. Thus, the listener should be null, and it should not be in the accessibility
// hierarchy.
- val container = initializeFingerprintContainer(
+ val container =
+ initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
addToView = addToView,
- )
+ )
waitForIdleSync()
assertThat(container.hasCredentialPasswordView()).isTrue()
@@ -615,26 +611,28 @@ open class AuthContainerViewTest : SysuiTestCase() {
addToView: Boolean = true,
verticalListContentView: PromptVerticalListContentView? = null,
contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null,
- ) = initializeContainer(
- TestAuthContainerView(
- authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal(),
+ ) =
+ initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
verticalListContentView = verticalListContentView,
- ),
- addToView
- )
+ ),
+ addToView
+ )
private fun initializeCoexContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
- ) = initializeContainer(
- TestAuthContainerView(
- authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal(),
- faceProps = faceSensorPropertiesInternal()
- ),
- addToView
- )
+ ) =
+ initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ faceProps = faceSensorPropertiesInternal()
+ ),
+ addToView
+ )
private fun initializeContainer(
view: TestAuthContainerView,
@@ -655,47 +653,50 @@ open class AuthContainerViewTest : SysuiTestCase() {
faceProps: List<FaceSensorPropertiesInternal> = listOf(),
verticalListContentView: PromptVerticalListContentView? = null,
contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null,
- ) : AuthContainerView(
- Config().apply {
- mContext = this@AuthContainerViewTest.context
- mCallback = callback
- mSensorIds = (fingerprintProps.map { it.sensorId } +
- faceProps.map { it.sensorId }).toIntArray()
- mSkipAnimation = true
- mPromptInfo = PromptInfo().apply {
- this.authenticators = authenticators
- if (verticalListContentView != null) {
- this.contentView = verticalListContentView
- } else if (contentViewWithMoreOptionsButton != null) {
- this.contentView = contentViewWithMoreOptionsButton
- }
- }
- mOpPackageName = OP_PACKAGE_NAME
- },
- testScope.backgroundScope,
- fingerprintProps,
- faceProps,
- wakefulnessLifecycle,
- panelInteractionDetector,
- userManager,
- lockPatternUtils,
- interactionJankMonitor,
- { promptSelectorInteractor },
- PromptViewModel(
- displayStateInteractor,
- promptSelectorInteractor,
- context,
- udfpsOverlayInteractor,
- biometricStatusInteractor,
- udfpsUtils,
- iconProvider,
- activityTaskManager
- ),
- { credentialViewModel },
- Handler(TestableLooper.get(this).looper),
- fakeExecutor,
- vibrator
- ) {
+ ) :
+ AuthContainerView(
+ Config().apply {
+ mContext = this@AuthContainerViewTest.context
+ mCallback = callback
+ mSensorIds =
+ (fingerprintProps.map { it.sensorId } + faceProps.map { it.sensorId })
+ .toIntArray()
+ mSkipAnimation = true
+ mPromptInfo =
+ PromptInfo().apply {
+ this.authenticators = authenticators
+ if (verticalListContentView != null) {
+ this.contentView = verticalListContentView
+ } else if (contentViewWithMoreOptionsButton != null) {
+ this.contentView = contentViewWithMoreOptionsButton
+ }
+ }
+ mOpPackageName = OP_PACKAGE_NAME
+ },
+ testScope.backgroundScope,
+ fingerprintProps,
+ faceProps,
+ wakefulnessLifecycle,
+ panelInteractionDetector,
+ userManager,
+ lockPatternUtils,
+ interactionJankMonitor,
+ { promptSelectorInteractor },
+ PromptViewModel(
+ displayStateInteractor,
+ promptSelectorInteractor,
+ context,
+ udfpsOverlayInteractor,
+ biometricStatusInteractor,
+ udfpsUtils,
+ iconProvider,
+ activityTaskManager
+ ),
+ { credentialViewModel },
+ Handler(TestableLooper.get(this).looper),
+ fakeExecutor,
+ vibrator
+ ) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
}
@@ -717,8 +718,10 @@ open class AuthContainerViewTest : SysuiTestCase() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
val lpFlags = layoutParams.flags
- assertThat((lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)
- != 0).isTrue()
+ assertThat(
+ (lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) != 0
+ )
+ .isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 3d63c5b6d0f8..13f2c7212e36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.graphics.Rect
+import android.hardware.fingerprint.FingerprintManager
import android.view.MotionEvent
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -51,6 +52,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
private lateinit var testScope: TestScope
+ @Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var authController: AuthController
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -111,6 +113,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
context,
authController,
selectedUserInteractor,
+ fingerprintManager,
testScope.backgroundScope
)
testScope.runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 42382540d401..7fa165c19f60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -73,6 +73,7 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.argumentCaptor
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -218,6 +219,13 @@ class SideFpsOverlayViewBinderTest : SysuiTestCase() {
verify(kosmos.windowManager).addView(any(), any())
+ var viewCaptor = argumentCaptor<View>()
+ verify(kosmos.windowManager).addView(viewCaptor.capture(), any())
+ verify(viewCaptor.firstValue)
+ .announceForAccessibility(
+ mContext.getText(R.string.accessibility_side_fingerprint_indicator_label)
+ )
+
// Hide alternate bouncer
kosmos.keyguardBouncerRepository.setAlternateVisible(false)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 93c6d9ee732c..314743c042c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -36,6 +36,7 @@ import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.platform.test.annotations.EnableFlags
@@ -114,6 +115,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock private lateinit var lockPatternUtils: LockPatternUtils
+ @Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var authController: AuthController
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var udfpsUtils: UdfpsUtils
@@ -202,6 +204,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
context,
authController,
selectedUserInteractor,
+ fingerprintManager,
testScope.backgroundScope
)
biometricStatusRepository = FakeBiometricStatusRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 6398a5af52f1..c1bd37811787 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -19,26 +19,22 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
-import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.whenever
+import org.junit.runners.JUnit4
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
@@ -54,35 +50,13 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
- givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
- stepToLockscreen(0f, TransitionState.STARTED),
- stepToLockscreen(.4f),
- stepToLockscreen(1f, TransitionState.FINISHED),
- ),
- testScope,
- )
- assertThat(canShowAlternateBouncer).isTrue()
- transitionRepository.sendTransitionSteps(
- listOf(
- stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
- stepFromLockscreenToAlternateBouncer(.4f),
- stepFromLockscreenToAlternateBouncer(.6f),
- ),
- testScope,
- )
- assertThat(canShowAlternateBouncer).isTrue()
- assertThat(alternateBouncerWindowRequired).isTrue()
-
- transitionRepository.sendTransitionSteps(
- listOf(
stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.2f),
+ stepFromAlternateBouncer(.4f),
stepFromAlternateBouncer(.6f),
),
testScope,
@@ -104,21 +78,13 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
- givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
- stepToLockscreen(0f, TransitionState.STARTED),
- stepToLockscreen(.4f),
- stepToLockscreen(1f, TransitionState.FINISHED),
- ),
- testScope,
- )
- transitionRepository.sendTransitionSteps(
- listOf(
- stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
- stepFromLockscreenToAlternateBouncer(.4f),
- stepFromLockscreenToAlternateBouncer(.6f),
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
),
testScope,
)
@@ -131,23 +97,13 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
- givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
- stepFromLockscreenToDozing(0f, TransitionState.STARTED),
- stepFromLockscreenToDozing(.4f),
- stepFromLockscreenToDozing(.6f),
- stepFromLockscreenToDozing(1f, TransitionState.FINISHED),
- ),
- testScope,
- )
- assertThat(alternateBouncerWindowRequired).isFalse()
- transitionRepository.sendTransitionSteps(
- listOf(
stepFromDozingToLockscreen(0f, TransitionState.STARTED),
stepFromDozingToLockscreen(.4f),
stepFromDozingToLockscreen(.6f),
+ stepFromDozingToLockscreen(1f),
),
testScope,
)
@@ -160,39 +116,19 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
- givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsRearFps()
transitionRepository.sendTransitionSteps(
listOf(
- stepToLockscreen(0f, TransitionState.STARTED),
- stepToLockscreen(.4f),
- stepToLockscreen(1f, TransitionState.FINISHED),
- ),
- testScope,
- )
- transitionRepository.sendTransitionSteps(
- listOf(
- stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
- stepFromLockscreenToAlternateBouncer(.4f),
- stepFromLockscreenToAlternateBouncer(.6f),
+ stepFromAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.6f),
+ stepFromAlternateBouncer(1f),
),
testScope,
)
assertThat(alternateBouncerWindowRequired).isFalse()
}
- private fun stepToLockscreen(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
- return step(
- from = KeyguardState.GONE,
- to = KeyguardState.LOCKSCREEN,
- value = value,
- transitionState = state,
- )
- }
-
private fun stepFromAlternateBouncer(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -205,18 +141,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
)
}
- private fun stepFromLockscreenToAlternateBouncer(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
- return step(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.ALTERNATE_BOUNCER,
- value = value,
- transitionState = state,
- )
- }
-
private fun stepFromDozingToLockscreen(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -229,18 +153,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
)
}
- private fun stepFromLockscreenToDozing(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
- return step(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING,
- value = value,
- transitionState = state,
- )
- }
-
private fun step(
from: KeyguardState,
to: KeyguardState,
@@ -255,16 +167,4 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
ownerName = "AlternateBouncerViewModelTest"
)
}
-
- /**
- * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside
- * from the fingerprint modality.
- */
- private fun givenCanShowAlternateBouncer() {
- kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
- kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
- kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
- whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
- whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 6f5c56eb9148..148a2e56f2a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlin.test.Test
@@ -60,9 +61,9 @@ class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
fun previewActionAccessed_beforeScreenshotCompleted_doesNothing() {
actionsProvider = createActionsProvider()
- val previewActionCaptor = argumentCaptor<() -> Unit>()
+ val previewActionCaptor = argumentCaptor<PreviewAction>()
verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
- previewActionCaptor.firstValue.invoke()
+ previewActionCaptor.firstValue.onClick.invoke()
verifyNoMoreInteractions(actionExecutor)
}
@@ -102,14 +103,14 @@ class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
fun actionAccessed_whilePending_launchesMostRecentAction() = runTest {
actionsProvider = createActionsProvider()
- val previewActionCaptor = argumentCaptor<() -> Unit>()
+ val previewActionCaptor = argumentCaptor<PreviewAction>()
verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
val actionButtonCaptor = argumentCaptor<() -> Unit>()
verify(actionsCallback, times(2))
.provideActionButton(any(), any(), actionButtonCaptor.capture())
actionButtonCaptor.firstValue.invoke()
- previewActionCaptor.firstValue.invoke()
+ previewActionCaptor.firstValue.onClick.invoke()
actionButtonCaptor.secondValue.invoke()
actionsProvider.setCompletedScreenshot(validResult)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
index 2a3c31aee6e7..a27c449fb86f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.screenshot
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import java.util.UUID
import kotlin.test.Test
@@ -35,7 +36,7 @@ class ScreenshotActionsControllerTest : SysuiTestCase() {
private val screenshotData = mock<ScreenshotData>()
private val actionExecutor = mock<ActionExecutor>()
private val viewModel = mock<ScreenshotViewModel>()
- private val onClick = mock<() -> Unit>()
+ private val previewAction = PreviewAction("description", onClick = {})
private lateinit var actionsController: ScreenshotActionsController
private lateinit var fakeActionsProvider1: FakeActionsProvider
@@ -43,6 +44,7 @@ class ScreenshotActionsControllerTest : SysuiTestCase() {
private val actionsProviderFactory =
object : ScreenshotActionsProvider.Factory {
var isFirstCall = true
+
override fun create(
requestId: UUID,
request: ScreenshotData,
@@ -69,16 +71,16 @@ class ScreenshotActionsControllerTest : SysuiTestCase() {
@Test
fun setPreview_onCurrentScreenshot_updatesViewModel() {
actionsController.setCurrentScreenshot(screenshotData)
- fakeActionsProvider1.callPreview(onClick)
+ fakeActionsProvider1.callPreview(previewAction)
- verify(viewModel).setPreviewAction(onClick)
+ verify(viewModel).setPreviewAction(previewAction)
}
@Test
fun setPreview_onNonCurrentScreenshot_doesNotUpdateViewModel() {
actionsController.setCurrentScreenshot(screenshotData)
actionsController.setCurrentScreenshot(screenshotData)
- fakeActionsProvider1.callPreview(onClick)
+ fakeActionsProvider1.callPreview(previewAction)
verify(viewModel, never()).setPreviewAction(any())
}
@@ -87,8 +89,8 @@ class ScreenshotActionsControllerTest : SysuiTestCase() {
private val actionsCallback: ScreenshotActionsController.ActionsCallback
) : ScreenshotActionsProvider {
- fun callPreview(onClick: () -> Unit) {
- actionsCallback.providePreviewAction(onClick)
+ fun callPreview(previewAction: PreviewAction) {
+ actionsCallback.providePreviewAction(previewAction)
}
override fun onScrollChipReady(onClick: Runnable) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index bf7d909380df..aca63f4a9e02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor as ArgumentCaptor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.lang.IllegalStateException
@@ -43,8 +42,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions
@SmallTest
class TakeScreenshotExecutorTest : SysuiTestCase() {
- private val controller0 = mock<ScreenshotController>()
- private val controller1 = mock<ScreenshotController>()
+ private val controller = mock<ScreenshotController>()
private val notificationsController0 = mock<ScreenshotNotificationsController>()
private val notificationsController1 = mock<ScreenshotNotificationsController>()
private val controllerFactory = mock<ScreenshotController.Factory>()
@@ -56,6 +54,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
private val topComponent = ComponentName(mContext, TakeScreenshotExecutorTest::class.java)
private val testScope = TestScope(UnconfinedTestDispatcher())
private val eventLogger = UiEventLoggerFake()
+ private val headlessHandler = mock<HeadlessScreenshotHandler>()
private val screenshotExecutor =
TakeScreenshotExecutorImpl(
@@ -64,14 +63,13 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
testScope,
requestProcessor,
eventLogger,
- notificationControllerFactory
+ notificationControllerFactory,
+ headlessHandler,
)
@Before
fun setUp() {
- whenever(controllerFactory.create(any(), any())).thenAnswer {
- if (it.getArgument<Display>(0).displayId == 0) controller0 else controller1
- }
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
whenever(notificationControllerFactory.create(eq(0))).thenReturn(notificationsController0)
whenever(notificationControllerFactory.create(eq(1))).thenReturn(notificationsController1)
}
@@ -86,14 +84,14 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
verify(controllerFactory).create(eq(internalDisplay), any())
- verify(controllerFactory).create(eq(externalDisplay), any())
+ verify(controllerFactory, never()).create(eq(externalDisplay), any())
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(0)
// OnSaved callback should be different.
- verify(controller1).handleScreenshot(capturer.capture(), any(), any())
+ verify(headlessHandler).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(1)
assertThat(eventLogger.numLogs()).isEqualTo(2)
@@ -125,10 +123,10 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(0)
// OnSaved callback should be different.
- verify(controller1, never()).handleScreenshot(any(), any(), any())
+ verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
assertThat(eventLogger.numLogs()).isEqualTo(1)
assertThat(eventLogger.get(0).eventId)
@@ -146,13 +144,14 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
verifyNoMoreInteractions(controllerFactory)
+ verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
screenshotExecutor.onDestroy()
}
@Test
fun executeScreenshots_allowedTypes_allCaptured() =
testScope.runTest {
- whenever(controllerFactory.create(any(), any())).thenReturn(controller0)
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
setDisplays(
display(TYPE_INTERNAL, id = 0),
@@ -163,7 +162,8 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
- verify(controller0, times(4)).handleScreenshot(any(), any(), any())
+ verify(controller, times(1)).handleScreenshot(any(), any(), any())
+ verify(headlessHandler, times(3)).handleScreenshot(any(), any(), any())
screenshotExecutor.onDestroy()
}
@@ -177,8 +177,8 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -202,8 +202,8 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), nullable(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -229,8 +229,8 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -254,50 +254,45 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onDestroy()
- verify(controller0).onDestroy()
- verify(controller1).onDestroy()
+ verify(controller).onDestroy()
}
@Test
- fun removeWindows_propagatedToControllers() =
+ fun removeWindows_propagatedToController() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.removeWindows()
- verify(controller0).removeWindow()
- verify(controller1).removeWindow()
+ verify(controller).removeWindow()
screenshotExecutor.onDestroy()
}
@Test
- fun onCloseSystemDialogsReceived_propagatedToControllers() =
+ fun onCloseSystemDialogsReceived_propagatedToController() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0).requestDismissal(any())
- verify(controller1).requestDismissal(any())
+ verify(controller).requestDismissal(any())
screenshotExecutor.onDestroy()
}
@Test
- fun onCloseSystemDialogsReceived_someControllerHavePendingTransitions() =
+ fun onCloseSystemDialogsReceived_controllerHasPendingTransitions() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
- whenever(controller0.isPendingSharedTransition).thenReturn(true)
- whenever(controller1.isPendingSharedTransition).thenReturn(false)
+ whenever(controller.isPendingSharedTransition).thenReturn(true)
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0, never()).requestDismissal(any())
- verify(controller1).requestDismissal(any())
+ verify(controller, never()).requestDismissal(any())
screenshotExecutor.onDestroy()
}
@@ -317,7 +312,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
.isEqualTo(ScreenshotData.fromRequest(screenshotRequest))
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value).isEqualTo(toBeReturnedByProcessor)
screenshotExecutor.onDestroy()
@@ -388,9 +383,9 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -408,9 +403,9 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -428,9 +423,9 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -449,7 +444,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
assertThat(it).isNull()
onSavedCallCount += 1
}
- whenever(controller0.handleScreenshot(any(), any(), any())).thenAnswer {
+ whenever(controller.handleScreenshot(any(), any(), any())).thenAnswer {
(it.getArgument(1) as Consumer<Uri?>).accept(null)
}
@@ -478,6 +473,7 @@ class TakeScreenshotExecutorTest : SysuiTestCase() {
var processed: ScreenshotData? = null
var toReturn: ScreenshotData? = null
var shouldThrowException = false
+
override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
if (shouldThrowException) throw RequestProcessorException("")
processed = screenshot
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
index 4945ace3b88c..c5708287a967 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
@@ -22,6 +22,7 @@ import android.os.UserHandle
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.screenshot.ImageCapture
import com.android.systemui.screenshot.ScreenshotData
import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
@@ -37,20 +38,23 @@ import org.junit.Test
class PolicyRequestProcessorTest {
- val imageCapture = object : ImageCapture {
- override fun captureDisplay(displayId: Int, crop: Rect?) = null
- override suspend fun captureTask(taskId: Int) = null
- }
+ val imageCapture =
+ object : ImageCapture {
+ override fun captureDisplay(displayId: Int, crop: Rect?) = null
+
+ override suspend fun captureTask(taskId: Int) = null
+ }
/** Tests behavior when no policies are applied */
@Test
fun testProcess_defaultOwner_whenNoPolicyApplied() {
val fullScreenWork = DisplayContentRepository {
- singleFullScreen(TaskSpec(taskId = 1001, name = FILES, userId = WORK))
+ singleFullScreen(TaskSpec(taskId = TASK_ID, name = FILES, userId = WORK))
}
val request =
- ScreenshotData(TAKE_SCREENSHOT_FULLSCREEN,
+ ScreenshotData(
+ TAKE_SCREENSHOT_FULLSCREEN,
SCREENSHOT_KEY_CHORD,
null,
topComponent = null,
@@ -58,24 +62,34 @@ class PolicyRequestProcessorTest {
taskId = -1,
insets = Insets.NONE,
bitmap = null,
- displayId = DEFAULT_DISPLAY)
+ displayId = DEFAULT_DISPLAY
+ )
/* Create a policy request processor with no capture policies */
val requestProcessor =
- PolicyRequestProcessor(Dispatchers.Unconfined,
+ PolicyRequestProcessor(
+ Dispatchers.Unconfined,
imageCapture,
policies = emptyList(),
defaultOwner = UserHandle.of(PERSONAL),
defaultComponent = ComponentName("default", "Component"),
- displayTasks = fullScreenWork)
+ displayTasks = fullScreenWork
+ )
val result = runBlocking { requestProcessor.process(request) }
- assertWithMessage(
- "With no policy, the screenshot should be assigned to the default user"
- ).that(result.userHandle).isEqualTo(UserHandle.of(PERSONAL))
+ assertWithMessage("With no policy, the screenshot should be assigned to the default user")
+ .that(result.userHandle)
+ .isEqualTo(UserHandle.of(PERSONAL))
+
+ assertWithMessage("The topComponent of the screenshot")
+ .that(result.topComponent)
+ .isEqualTo(ComponentName.unflattenFromString(FILES))
+
+ assertWithMessage("Task ID").that(result.taskId).isEqualTo(TASK_ID)
+ }
- assertWithMessage("The topComponent of the screenshot").that(result.topComponent)
- .isEqualTo(ComponentName.unflattenFromString(FILES))
+ companion object {
+ const val TASK_ID = 1001
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
index 0dd988d424b9..40f7b7fb9968 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
@@ -56,7 +56,7 @@ class NotificationSettingsRepositoryTest : SysuiTestCase() {
@Test
fun testGetIsShowNotificationsOnLockscreenEnabled() =
testScope.runTest {
- val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
secureSettingsRepository.setInt(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
@@ -74,7 +74,7 @@ class NotificationSettingsRepositoryTest : SysuiTestCase() {
@Test
fun testSetIsShowNotificationsOnLockscreenEnabled() =
testScope.runTest {
- val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
underTest.setShowNotificationsOnLockscreenEnabled(true)
assertThat(showNotifs).isEqualTo(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 890a2e40c94c..f7ff568332ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -32,6 +32,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CO
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR
import android.telephony.satellite.SatelliteManager.SatelliteException
import android.telephony.satellite.SatelliteModemStateCallback
import android.telephony.satellite.SatelliteProvisionStateCallback
@@ -62,6 +63,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -354,13 +356,142 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun satelliteProvisioned_supported_tracksCallback() =
+ fun satelliteProvisioned_returnsException_defaultsToFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported on device
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(true)
+ }
+ .whenever(satelliteManager)
+ .requestIsSupported(any(), any())
+
+ // GIVEN satellite returns an error when asked if provisioned
+ doAnswer {
+ val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+ receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR))
+ null
+ }
+ .whenever(satelliteManager)
+ .requestIsProvisioned(
+ any(),
+ any<OutcomeReceiver<Boolean, SatelliteException>>()
+ )
+
+ // GIVEN we've been up long enough to start querying
+ systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
+
+ underTest =
+ DeviceBasedSatelliteRepositoryImpl(
+ Optional.of(satelliteManager),
+ telephonyManager,
+ dispatcher,
+ testScope.backgroundScope,
+ logBuffer = FakeLogBuffer.Factory.create(),
+ verboseLogBuffer = FakeLogBuffer.Factory.create(),
+ systemClock,
+ )
+
+ // WHEN we try to check for provisioned status
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ // THEN well, first we don't throw...
+ // AND THEN we assume that it's not provisioned
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_throwsWhenQuerying_defaultsToFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported on device
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(true)
+ }
+ .whenever(satelliteManager)
+ .requestIsSupported(any(), any())
+
+ // GIVEN satellite throws when asked if provisioned
+ whenever(satelliteManager.requestIsProvisioned(any(), any()))
+ .thenThrow(SecurityException())
+
+ // GIVEN we've been up long enough to start querying
+ systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
+
+ underTest =
+ DeviceBasedSatelliteRepositoryImpl(
+ Optional.of(satelliteManager),
+ telephonyManager,
+ dispatcher,
+ testScope.backgroundScope,
+ logBuffer = FakeLogBuffer.Factory.create(),
+ verboseLogBuffer = FakeLogBuffer.Factory.create(),
+ systemClock,
+ )
+
+ // WHEN we try to check for provisioned status
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ // THEN well, first we don't throw...
+ // AND THEN we assume that it's not provisioned
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacks() =
+ testScope.runTest {
+ // GIVEN satellite is supported, and provisioned
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ initialSatelliteIsProvisioned = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ // THEN the current state is requested
+ verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
+
+ // AND the state is correct
+ assertThat(provisioned).isTrue()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacks() =
+ testScope.runTest {
+ // GIVEN satellite is supported, and provisioned
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ initialSatelliteIsProvisioned = false,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ // THEN the current state is requested
+ verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
+
+ // AND the state is correct
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallback() =
testScope.runTest {
// GIVEN satellite is not supported
setUpRepo(
uptime = MIN_UPTIME,
satMan = satelliteManager,
satelliteSupported = true,
+ initialSatelliteIsProvisioned = false,
)
val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
@@ -416,6 +547,8 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
// THEN listeners are re-registered
verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
+ // AND the state is queried again
+ verify(satelliteManager, times(2)).requestIsProvisioned(any(), any())
}
@Test
@@ -632,6 +765,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
uptime: Long = MIN_UPTIME,
satMan: SatelliteManager? = satelliteManager,
satelliteSupported: Boolean = true,
+ initialSatelliteIsProvisioned: Boolean = true,
) {
doAnswer {
val callback: OutcomeReceiver<Boolean, SatelliteException> =
@@ -641,6 +775,14 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
.whenever(satelliteManager)
.requestIsSupported(any(), any())
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(initialSatelliteIsProvisioned)
+ }
+ .whenever(satelliteManager)
+ .requestIsProvisioned(any(), any())
+
systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime)
underTest =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 9dae44dc8053..7c53639a85a6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -18,6 +18,7 @@ package com.android.systemui
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.app.trust.TrustManager
+import android.hardware.fingerprint.FingerprintManager
import android.os.UserManager
import android.service.notification.NotificationListenerService
import android.util.DisplayMetrics
@@ -94,6 +95,7 @@ data class TestMocksModule(
@get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
@get:Provides val dozeParameters: DozeParameters = mock(),
@get:Provides val dumpManager: DumpManager = mock(),
+ @get:Provides val fingerprintManager: FingerprintManager = mock(),
@get:Provides val headsUpManager: HeadsUpManager = mock(),
@get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
index cbfc7686a896..ae592b968f8b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
@@ -17,17 +17,20 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.applicationContext
+import android.hardware.fingerprint.FingerprintManager
import com.android.systemui.biometrics.authController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
val Kosmos.udfpsOverlayInteractor by Fixture {
UdfpsOverlayInteractor(
context = applicationContext,
authController = authController,
selectedUserInteractor = selectedUserInteractor,
+ fingerprintManager = mock<FingerprintManager>(),
scope = applicationCoroutineScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 21d59f09711c..135cb14a3497 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -22,7 +22,6 @@ import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -42,6 +41,7 @@ class FakeAudioRepository : AudioRepository {
private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf()
private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf()
+ private val deviceCategories: MutableMap<String, Int> = mutableMapOf()
private fun getAudioStreamModelState(
audioStream: AudioStream
@@ -60,7 +60,7 @@ class FakeAudioRepository : AudioRepository {
)
}
- override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
+ override fun getAudioStream(audioStream: AudioStream): StateFlow<AudioStreamModel> =
getAudioStreamModelState(audioStream).asStateFlow()
override suspend fun setVolume(audioStream: AudioStream, volume: Int) {
@@ -103,4 +103,12 @@ class FakeAudioRepository : AudioRepository {
override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
mutableRingerMode.value = mode
}
+
+ fun setBluetoothAudioDeviceCategory(bluetoothAddress: String, category: Int) {
+ deviceCategories[bluetoothAddress] = category
+ }
+
+ override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int {
+ return deviceCategories[bluetoothAddress] ?: AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
index 141f2426f365..83adc799b471 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -18,9 +18,11 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto
import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.graphics.drawable.Drawable
import android.graphics.drawable.TestStubDrawable
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -59,11 +61,17 @@ object TestMediaDevicesFactory {
whenever(name).thenReturn(deviceName)
whenever(address).thenReturn(deviceAddress)
}
+ val leAudioProfile =
+ mock<LeAudioProfile> {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(true)
+ }
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(isHearingAidDevice).thenReturn(true)
whenever(address).thenReturn(deviceAddress)
whenever(device).thenReturn(bluetoothDevice)
whenever(name).thenReturn(deviceName)
+ whenever(profiles).thenReturn(listOf(leAudioProfile))
}
return mock<BluetoothMediaDevice> {
whenever(name).thenReturn(deviceName)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
index 95a7b9bb185b..6a46d56ba55a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
+import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.domain.interactor.audioOutputInteractor
val Kosmos.spatialAudioComponentInteractor by
@@ -26,6 +28,8 @@ val Kosmos.spatialAudioComponentInteractor by
SpatialAudioComponentInteractor(
audioOutputInteractor,
spatializerInteractor,
+ audioRepository,
+ backgroundCoroutineContext,
testScope.backgroundScope,
)
}
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 fec6ff17a608..0458f535c1eb 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
@@ -28,6 +28,9 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.atomic.AtomicInteger
+
+private const val INVALID_ROTATION = -1
/**
* Allows to subscribe to rotation changes. Updates are provided for the display associated to
@@ -45,7 +48,7 @@ constructor(
private val listeners = CopyOnWriteArrayList<RotationListener>()
private val displayListener = RotationDisplayListener()
- private var lastRotation: Int? = null
+ private val lastRotation = AtomicInteger(INVALID_ROTATION)
override fun addCallback(listener: RotationListener) {
bgHandler.post {
@@ -61,7 +64,7 @@ constructor(
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
- lastRotation = null
+ lastRotation.set(INVALID_ROTATION)
}
}
}
@@ -100,9 +103,8 @@ constructor(
if (displayId == display.displayId) {
val currentRotation = display.rotation
- if (lastRotation == null || lastRotation != currentRotation) {
+ if (lastRotation.compareAndSet(lastRotation.get(), currentRotation)) {
listeners.forEach { it.onRotationChanged(currentRotation) }
- lastRotation = currentRotation
}
}
} finally {
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index d7da2f0052d3..443bb745ad84 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -685,15 +685,6 @@ public final class PresentationStatsEventLogger {
}
/**
- * Set how many views are filtered from fill because they are not in current session
- */
- public void maybeSetFilteredFillableViewsCount(int filteredViewsCount) {
- mEventInternal.ifPresent(event -> {
- event.mFilteredFillabaleViewCount = filteredViewsCount;
- });
- }
-
- /**
* Set views_filled_failure_count using failure count as long as mEventInternal
* presents.
*/
@@ -789,7 +780,6 @@ public final class PresentationStatsEventLogger {
+ " mAppPackageUid=" + mCallingAppUid
+ " mIsCredentialRequest=" + event.mIsCredentialRequest
+ " mWebviewRequestedCredential=" + event.mWebviewRequestedCredential
- + " mFilteredFillabaleViewCount=" + event.mFilteredFillabaleViewCount
+ " mViewFillableTotalCount=" + event.mViewFillableTotalCount
+ " mViewFillFailureCount=" + event.mViewFillFailureCount
+ " mFocusedId=" + event.mFocusedId
@@ -846,7 +836,6 @@ public final class PresentationStatsEventLogger {
mCallingAppUid,
event.mIsCredentialRequest,
event.mWebviewRequestedCredential,
- event.mFilteredFillabaleViewCount,
event.mViewFillableTotalCount,
event.mViewFillFailureCount,
event.mFocusedId,
@@ -895,7 +884,6 @@ public final class PresentationStatsEventLogger {
int mFieldClassificationRequestId = DEFAULT_VALUE_INT;
boolean mIsCredentialRequest = false;
boolean mWebviewRequestedCredential = false;
- int mFilteredFillabaleViewCount = DEFAULT_VALUE_INT;
int mViewFillableTotalCount = DEFAULT_VALUE_INT;
int mViewFillFailureCount = DEFAULT_VALUE_INT;
int mFocusedId = DEFAULT_VALUE_INT;
diff --git a/services/autofill/java/com/android/server/autofill/RequestId.java b/services/autofill/java/com/android/server/autofill/RequestId.java
index 29ad786dbd4b..d8069a840156 100644
--- a/services/autofill/java/com/android/server/autofill/RequestId.java
+++ b/services/autofill/java/com/android/server/autofill/RequestId.java
@@ -16,8 +16,14 @@
package com.android.server.autofill;
-import java.util.List;
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.util.Slog;
+import android.util.SparseArray;
+
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.List;
+import java.util.Random;
// Helper class containing various methods to deal with FillRequest Ids.
// For authentication flows, there needs to be a way to know whether to retrieve the Fill
@@ -25,56 +31,97 @@ import java.util.concurrent.atomic.AtomicInteger;
// way to achieve this is by assigning odd number request ids to secondary provider and
// even numbers to primary provider.
public class RequestId {
+ private AtomicInteger sIdCounter;
+
+ // The minimum request id is 2 to avoid possible authentication issues.
+ static final int MIN_REQUEST_ID = 2;
+ // The maximum request id is 0x7FFF to make sure the 16th bit is 0.
+ // This is to make sure the authentication id is always positive.
+ static final int MAX_REQUEST_ID = 0x7FFF; // 32767
+
+ // The maximum start id is made small to best avoid wrapping around.
+ static final int MAX_START_ID = 1000;
+ // The magic number is used to determine if a wrap has happened.
+ // The underlying assumption of MAGIC_NUMBER is that there can't be as many as MAGIC_NUMBER
+ // of fill requests in one session. so there can't be as many as MAGIC_NUMBER of fill requests
+ // getting dropped.
+ static final int MAGIC_NUMBER = 5000;
+
+ static final int MIN_PRIMARY_REQUEST_ID = 2;
+ static final int MAX_PRIMARY_REQUEST_ID = 0x7FFE; // 32766
+
+ static final int MIN_SECONDARY_REQUEST_ID = 3;
+ static final int MAX_SECONDARY_REQUEST_ID = 0x7FFF; // 32767
+
+ private static final String TAG = "RequestId";
+
+ // WARNING: This constructor should only be used for testing
+ RequestId(int startId) {
+ if (startId < MIN_REQUEST_ID || startId > MAX_REQUEST_ID) {
+ throw new IllegalArgumentException("startId must be between " + MIN_REQUEST_ID +
+ " and " + MAX_REQUEST_ID);
+ }
+ if (sDebug) {
+ Slog.d(TAG, "RequestId(int): startId= " + startId);
+ }
+ sIdCounter = new AtomicInteger(startId);
+ }
- private AtomicInteger sIdCounter;
-
- // Mainly used for tests
- RequestId(int start) {
- sIdCounter = new AtomicInteger(start);
- }
-
- public RequestId() {
- this((int) (Math.floor(Math.random() * 0xFFFF)));
- }
-
- public static int getLastRequestIdIndex(List<Integer> requestIds) {
- int lastId = -1;
- int indexOfBiggest = -1;
- // Biggest number is usually the latest request, since IDs only increase
- // The only exception is when the request ID wraps around back to 0
- for (int i = requestIds.size() - 1; i >= 0; i--) {
- if (requestIds.get(i) > lastId) {
- lastId = requestIds.get(i);
- indexOfBiggest = i;
- }
+ // WARNING: This get method should only be used for testing
+ int getRequestId() {
+ return sIdCounter.get();
}
- // 0xFFFE + 2 == 0x1 (for secondary)
- // 0xFFFD + 2 == 0x0 (for primary)
- // Wrap has occurred
- if (lastId >= 0xFFFD) {
- // Calculate the biggest size possible
- // If list only has one kind of request ids - we need to multiple by 2
- // (since they skip odd ints)
- // Also subtract one from size because at least one integer exists pre-wrap
- int calcSize = (requestIds.size()) * 2;
- //Biggest possible id after wrapping
- int biggestPossible = (lastId + calcSize) % 0xFFFF;
- lastId = -1;
- indexOfBiggest = -1;
- for (int i = 0; i < requestIds.size(); i++) {
- int currentId = requestIds.get(i);
- if (currentId <= biggestPossible && currentId > lastId) {
- lastId = currentId;
- indexOfBiggest = i;
+ public RequestId() {
+ Random random = new Random();
+ int low = MIN_REQUEST_ID;
+ int high = MAX_START_ID + 1; // nextInt is exclusive on upper limit
+
+ // Generate a random start request id that >= MIN_REQUEST_ID and <= MAX_START_ID
+ int startId = random.nextInt(high - low) + low;
+ if (sDebug) {
+ Slog.d(TAG, "RequestId(): startId= " + startId);
}
- }
+ sIdCounter = new AtomicInteger(startId);
}
- return indexOfBiggest;
- }
+ // Given a list of request ids, find the index of the last request id.
+ // Note: Since the request id wraps around, the largest request id may not be
+ // the latest request id.
+ //
+ // @param requestIds List of request ids in ascending order with at least one element.
+ // @return Index of the last request id.
+ public static int getLastRequestIdIndex(List<Integer> requestIds) {
+ // If there is only one request id, return index as 0.
+ if (requestIds.size() == 1) {
+ return 0;
+ }
+
+ // We have to use a magical number to determine if a wrap has happened because
+ // the request id could be lost. The underlying assumption of MAGIC_NUMBER is that
+ // there can't be as many as MAGIC_NUMBER of fill requests in one session.
+ boolean wrapHasHappened = false;
+ int latestRequestIdIndex = -1;
+
+ for (int i = 0; i < requestIds.size() - 1; i++) {
+ if (requestIds.get(i+1) - requestIds.get(i) > MAGIC_NUMBER) {
+ wrapHasHappened = true;
+ latestRequestIdIndex = i;
+ break;
+ }
+ }
+
+ // If there was no wrap, the last request index is the last index.
+ if (!wrapHasHappened) {
+ latestRequestIdIndex = requestIds.size() - 1;
+ }
+ if (sDebug) {
+ Slog.d(TAG, "getLastRequestIdIndex(): latestRequestIdIndex = " + latestRequestIdIndex);
+ }
+ return latestRequestIdIndex;
+ }
- public int nextId(boolean isSecondary) {
+ public int nextId(boolean isSecondary) {
// For authentication flows, there needs to be a way to know whether to retrieve the Fill
// Response from the primary provider or the secondary provider from the requestId. A simple
// way to achieve this is by assigning odd number request ids to secondary provider and
@@ -82,13 +129,20 @@ public class RequestId {
int requestId;
do {
- requestId = sIdCounter.incrementAndGet() % 0xFFFF;
+ requestId = sIdCounter.incrementAndGet() % (MAX_REQUEST_ID + 1);
+ // Skip numbers smaller than MIN_REQUEST_ID to avoid possible authentication issue
+ if (requestId < MIN_REQUEST_ID) {
+ requestId = MIN_REQUEST_ID;
+ }
sIdCounter.set(requestId);
} while (isSecondaryProvider(requestId) != isSecondary);
+ if (sDebug) {
+ Slog.d(TAG, "nextId(): requestId = " + requestId);
+ }
return requestId;
- }
+ }
- public static boolean isSecondaryProvider(int requestId) {
- return requestId % 2 == 1;
- }
+ public static boolean isSecondaryProvider(int requestId) {
+ return requestId % 2 == 1;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 494e956c413f..4852478281d8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -6641,8 +6641,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
boolean waitingDatasetAuth = false;
boolean hideHighlight = (entryCount == 1
&& dataset.getFieldIds().get(0).equals(mCurrentViewId));
- // Count how many views are filtered because they are not in current session
- int numOfViewsFiltered = 0;
for (int i = 0; i < entryCount; i++) {
if (dataset.getFieldValues().get(i) == null) {
continue;
@@ -6655,7 +6653,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "Skipping filling view: " +
viewId + " as it isn't part of the current session: " + id);
}
- numOfViewsFiltered += 1;
continue;
}
ids.add(viewId);
@@ -6669,8 +6666,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH);
}
}
- mPresentationStatsEventLogger.maybeSetFilteredFillableViewsCount(
- numOfViewsFiltered);
if (!ids.isEmpty()) {
if (waitingDatasetAuth) {
mUi.hideFillUi(this);
@@ -6902,17 +6897,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
+ // Return latest response index in mResponses SparseArray.
@GuardedBy("mLock")
private int getLastResponseIndexLocked() {
- if (mResponses != null) {
- List<Integer> requestIdList = new ArrayList<>();
- final int responseCount = mResponses.size();
- for (int i = 0; i < responseCount; i++) {
- requestIdList.add(mResponses.keyAt(i));
- }
- return mRequestId.getLastRequestIdIndex(requestIdList);
+ if (mResponses == null || mResponses.size() == 0) {
+ return -1;
+ }
+ List<Integer> requestIdList = new ArrayList<>();
+ final int responseCount = mResponses.size();
+ for (int i = 0; i < responseCount; i++) {
+ requestIdList.add(mResponses.keyAt(i));
}
- return -1;
+ return mRequestId.getLastRequestIdIndex(requestIdList);
}
private LogMaker newLogMaker(int category) {
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 4860a274cfa8..8abbe5666d58 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -792,10 +792,11 @@ public class TarBackupReader {
}
private String getVToUAllowlist(Context context, int userId) {
- return Settings.Secure.getStringForUser(
+ String allowlist = Settings.Secure.getStringForUser(
context.getContentResolver(),
Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
userId);
+ return (allowlist == null) ? "" : allowlist;
}
private static long extractRadix(byte[] data, int offset, int maxChars, int radix)
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index b64aa8a1cbc9..ea6351baf597 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -364,7 +364,7 @@ public class ContextualSearchManagerService extends SystemService {
}
@RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
- private int invokeContextualSearchIntent(Intent launchIntent) {
+ private int invokeContextualSearchIntent(Intent launchIntent, final int userId) {
// Contextual search starts with a frozen screen - so we launch without
// any system animations or starting window.
final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
@@ -372,7 +372,7 @@ public class ContextualSearchManagerService extends SystemService {
opts.setDisableStartingWindow(true);
return mAtmInternal.startActivityWithScreenshot(launchIntent,
mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
- opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+ opts.toBundle(), userId);
}
private void enforcePermission(@NonNull final String func) {
@@ -446,6 +446,8 @@ public class ContextualSearchManagerService extends SystemService {
synchronized (this) {
if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
enforcePermission("startContextualSearch");
+ final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+
mAssistDataRequester.cancel();
// Creates a new CallbackToken at mToken and an expiration handler.
issueToken();
@@ -455,7 +457,7 @@ public class ContextualSearchManagerService extends SystemService {
Binder.withCleanCallingIdentity(() -> {
Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
if (launchIntent != null) {
- int result = invokeContextualSearchIntent(launchIntent);
+ int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
}
});
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 44aea15b2bde..e2ab0d9f2683 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1603,7 +1603,7 @@ public class SystemConfig {
} else {
mPackageToSharedUidAllowList.put(pkgName, sharedUid);
}
- }
+ } break;
case "asl-file": {
String packageName = parser.getAttributeValue(null, "package");
String path = parser.getAttributeValue(null, "path");
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1bb792227798..f61bd6040658 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2790,8 +2790,9 @@ public class AppOpsService extends IAppOpsService.Stub {
* have information on them.
*/
private static boolean isOpAllowedForUid(int uid) {
+ int appId = UserHandle.getAppId(uid);
return Flags.runtimePermissionAppopsMappingEnabled()
- && (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID);
+ && (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID);
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0b3b1112e2ed..8e29de773a77 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2799,12 +2799,13 @@ public class AudioDeviceBroker {
return mDeviceInventory.getImmutableDeviceInventory();
}
- void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
- mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState);
+ void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory) {
+ mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState, syncInventory);
}
- void addOrUpdateBtAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
- mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ void addOrUpdateBtAudioDeviceCategoryInInventory(
+ AdiDeviceState deviceState, boolean syncInventory) {
+ mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState, syncInventory);
}
@Nullable
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ba7aee05159a..6ff4a617cec4 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -135,9 +135,10 @@ public class AudioDeviceInventory {
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
- void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
+ void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory) {
synchronized (mDeviceInventoryLock) {
- mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> {
+ mDeviceInventory.merge(deviceState.getDeviceId(), deviceState,
+ (oldState, newState) -> {
oldState.setHasHeadTracker(newState.hasHeadTracker());
oldState.setHeadTrackerEnabled(newState.isHeadTrackerEnabled());
oldState.setSAEnabled(newState.isSAEnabled());
@@ -145,7 +146,9 @@ public class AudioDeviceInventory {
});
checkDeviceInventorySize_l();
}
- mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ if (syncInventory) {
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ }
}
/**
@@ -196,7 +199,8 @@ public class AudioDeviceInventory {
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
- void addOrUpdateAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
+ void addOrUpdateAudioDeviceCategoryInInventory(
+ AdiDeviceState deviceState, boolean syncInventory) {
AtomicBoolean updatedCategory = new AtomicBoolean(false);
synchronized (mDeviceInventoryLock) {
if (automaticBtDeviceType()) {
@@ -218,7 +222,9 @@ public class AudioDeviceInventory {
if (updatedCategory.get()) {
mDeviceBroker.postUpdatedAdiDeviceState(deviceState, false /*initSA*/);
}
- mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ if (syncInventory) {
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ }
}
void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
@@ -235,14 +241,14 @@ public class AudioDeviceInventory {
boolean bleCategoryFound = false;
AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET);
if (deviceState != null) {
- addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/);
btCategory = deviceState.getAudioDeviceCategory();
bleCategoryFound = true;
}
deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP);
if (deviceState != null) {
- addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/);
int a2dpCategory = deviceState.getAudioDeviceCategory();
if (bleCategoryFound && a2dpCategory != btCategory) {
Log.w(TAG, "Found different audio device category for A2DP and BLE profiles with "
@@ -269,23 +275,43 @@ public class AudioDeviceInventory {
}
/**
- * synchronize AdiDeviceState for LE devices in the same group
+ * Synchronize AdiDeviceState for LE devices in the same group
+ * or BT classic devices with the same address.
+ * @param updatedDevice the device state to synchronize or null.
+ * Called with null once after the device inventory and spatializer helper
+ * have been initialized to resync all devices.
*/
void onSynchronizeAdiDevicesInInventory(AdiDeviceState updatedDevice) {
synchronized (mDevicesLock) {
synchronized (mDeviceInventoryLock) {
- boolean found = false;
- found |= synchronizeBleDeviceInInventory(updatedDevice);
- if (automaticBtDeviceType()) {
- found |= synchronizeDeviceProfilesInInventory(updatedDevice);
- }
- if (found) {
- mDeviceBroker.postPersistAudioDeviceSettings();
+ if (updatedDevice != null) {
+ onSynchronizeAdiDeviceInInventory_l(updatedDevice);
+ } else {
+ for (AdiDeviceState ads : mDeviceInventory.values()) {
+ onSynchronizeAdiDeviceInInventory_l(ads);
+ }
}
}
}
}
+ /**
+ * Synchronize AdiDeviceState for LE devices in the same group
+ * or BT classic devices with the same address.
+ * @param updatedDevice the device state to synchronize.
+ */
+ @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"})
+ void onSynchronizeAdiDeviceInInventory_l(AdiDeviceState updatedDevice) {
+ boolean found = false;
+ found |= synchronizeBleDeviceInInventory(updatedDevice);
+ if (automaticBtDeviceType()) {
+ found |= synchronizeDeviceProfilesInInventory(updatedDevice);
+ }
+ if (found) {
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ }
+
@GuardedBy("mDeviceInventoryLock")
private void checkDeviceInventorySize_l() {
if (mDeviceInventory.size() > MAX_DEVICE_INVENTORY_ENTRIES) {
@@ -595,6 +621,9 @@ public class AudioDeviceInventory {
mDeviceName = TextUtils.emptyIfNull(deviceName);
mDeviceAddress = TextUtils.emptyIfNull(address);
mDeviceIdentityAddress = TextUtils.emptyIfNull(identityAddress);
+ if (mDeviceIdentityAddress.isEmpty()) {
+ mDeviceIdentityAddress = mDeviceAddress;
+ }
mDeviceCodecFormat = codecFormat;
mGroupId = groupId;
mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress);
@@ -2951,8 +2980,8 @@ public class AudioDeviceInventory {
// Note if the device is not compatible with spatialization mode or the device
// type is not canonical, it will be ignored in {@link SpatializerHelper}.
if (devState != null) {
- addOrUpdateDeviceSAStateInInventory(devState);
- addOrUpdateAudioDeviceCategoryInInventory(devState);
+ addOrUpdateDeviceSAStateInInventory(devState, false /*syncInventory*/);
+ addOrUpdateAudioDeviceCategoryInInventory(devState, false /*syncInventory*/);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 64452a979edd..b0590fe70232 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -707,10 +707,14 @@ public class AudioService extends IAudioService.Stub
// Streams currently muted by ringer mode and dnd
protected static volatile int sRingerAndZenModeMutedStreams;
- /** Streams that can be muted. Do not resolve to aliases when checking.
+ /** Streams that can be muted by system. Do not resolve to aliases when checking.
* @see System#MUTE_STREAMS_AFFECTED */
private int mMuteAffectedStreams;
+ /** Streams that can be muted by user. Do not resolve to aliases when checking.
+ * @see System#MUTE_STREAMS_AFFECTED */
+ private int mUserMutableStreams;
+
@NonNull
private SoundEffectsHelper mSfxHelper;
@@ -2343,6 +2347,7 @@ public class AudioService extends IAudioService.Stub
mMuteAffectedStreams &= ~(1 << vss.mStreamType);
}
}
+ updateUserMutableStreams();
}
private void createStreamStates() {
@@ -2412,6 +2417,8 @@ public class AudioService extends IAudioService.Stub
}
pw.print("\n- mute affected streams = 0x");
pw.println(Integer.toHexString(mMuteAffectedStreams));
+ pw.print("\n- user mutable streams = 0x");
+ pw.println(Integer.toHexString(mUserMutableStreams));
}
private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
@@ -2906,6 +2913,7 @@ public class AudioService extends IAudioService.Stub
mMuteAffectedStreams = mSettings.getSystemIntForUser(cr,
System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
UserHandle.USER_CURRENT);
+ updateUserMutableStreams();
updateMasterMono(cr);
@@ -2925,6 +2933,12 @@ public class AudioService extends IAudioService.Stub
mVolumeController.loadSettings(cr);
}
+ private void updateUserMutableStreams() {
+ mUserMutableStreams = mMuteAffectedStreams;
+ mUserMutableStreams &= ~(1 << AudioSystem.STREAM_VOICE_CALL);
+ mUserMutableStreams &= ~(1 << AudioSystem.STREAM_BLUETOOTH_SCO);
+ }
+
@GuardedBy("mSettingsLock")
private void resetActiveAssistantUidsLocked() {
mActiveAssistantServiceUids = NO_ACTIVE_ASSISTANT_SERVICE_UIDS;
@@ -7133,6 +7147,11 @@ public class AudioService extends IAudioService.Stub
return (mMuteAffectedStreams & (1 << streamType)) != 0;
}
+ @Override
+ public boolean isStreamMutableByUi(int streamType) {
+ return (mUserMutableStreams & (1 << streamType)) != 0;
+ }
+
private void ensureValidDirection(int direction) {
switch (direction) {
case AudioManager.ADJUST_LOWER:
@@ -9644,6 +9663,9 @@ public class AudioService extends IAudioService.Stub
case MSG_INIT_SPATIALIZER:
onInitSpatializer();
+ // the device inventory can only be synchronized after the
+ // spatializer has been initialized
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(null);
mAudioEventWakeLock.release();
break;
@@ -11399,7 +11421,8 @@ public class AudioService extends IAudioService.Stub
deviceState.setAudioDeviceCategory(btAudioDeviceCategory);
- mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
+ mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(
+ deviceState, true /*syncInventory*/);
mDeviceBroker.postPersistAudioDeviceSettings();
mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes(),
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index cae169550d9a..9265ff2d9b2d 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -568,7 +568,8 @@ public class SpatializerHelper {
updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
ada.getAddress());
initSAState(updatedDevice);
- mDeviceBroker.addOrUpdateDeviceSAStateInInventory(updatedDevice);
+ mDeviceBroker.addOrUpdateDeviceSAStateInInventory(
+ updatedDevice, true /*syncInventory*/);
}
if (updatedDevice != null) {
onRoutingUpdated();
@@ -723,7 +724,7 @@ public class SpatializerHelper {
new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
ada.getAddress());
initSAState(deviceState);
- mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState);
+ mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState, true /*syncInventory*/);
mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index d061e2d21811..fbd32a67fe6c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -31,7 +31,6 @@ import android.util.Slog;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import java.util.function.Supplier;
@@ -203,16 +202,6 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
}
}
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- protected final void resetIgnoreDisplayTouches() {
- final AidlSession session = (AidlSession) getFreshDaemon();
- try {
- session.getSession().setIgnoreDisplayTouches(false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when resetting setIgnoreDisplayTouches");
- }
- }
-
@Override
public boolean isInterruptable() {
return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 4c86f57a190b..60cfd5a5a6ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -962,6 +962,19 @@ public class FingerprintService extends SystemService {
provider.onUdfpsUiEvent(event, requestId, sensorId);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) {
+ super.setIgnoreDisplayTouches_enforcePermission();
+
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG,
+ "No matching provider for setIgnoreDisplayTouches, sensorId: " + sensorId);
+ return;
+ }
+ provider.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouches);
+ }
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index a6cf2f4b31ae..e4a99e6be72c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -134,6 +134,8 @@ public interface ServiceProvider extends
void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
+ void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches);
+
void onPowerPressed();
@NonNull
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index dce0175ca593..15d7a476b345 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -31,4 +31,5 @@ public interface Udfps {
void onPointerUp(PointerContext pc);
void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event);
boolean isPointerDown();
+ void setIgnoreDisplayTouches(boolean ignoreTouches);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 72d92b974c1a..d04afdb72913 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -33,7 +33,6 @@ import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcqu
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.events.AuthenticationAcquiredInfo;
import android.hardware.biometrics.events.AuthenticationErrorInfo;
import android.hardware.biometrics.events.AuthenticationFailedInfo;
@@ -182,7 +181,6 @@ public class FingerprintAuthenticationClient
handleLockout(authenticated);
if (authenticated) {
mState = STATE_STOPPED;
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (reportBiometricAuthAttempts()) {
mAuthenticationStateListeners.onAuthenticationSucceeded(
@@ -223,7 +221,6 @@ public class FingerprintAuthenticationClient
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationError(
new AuthenticationErrorInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -275,7 +272,6 @@ public class FingerprintAuthenticationClient
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -284,7 +280,6 @@ public class FingerprintAuthenticationClient
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), getRequestReason(), this);
mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -331,12 +326,6 @@ public class FingerprintAuthenticationClient
if (session.hasContextMethods()) {
try {
session.getSession().onContextChanged(ctx);
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- if (ctx.operationState != null && ctx.operationState.getTag()
- == OperationState.fingerprintOperationState) {
- session.getSession().setIgnoreDisplayTouches(ctx.operationState
- .getFingerprintOperationState().isHardwareIgnoringTouches);
- }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify context changed", e);
}
@@ -353,7 +342,6 @@ public class FingerprintAuthenticationClient
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -415,6 +403,15 @@ public class FingerprintAuthenticationClient
}
@Override
+ public void setIgnoreDisplayTouches(boolean ignoreTouches) {
+ try {
+ getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@@ -457,7 +454,6 @@ public class FingerprintAuthenticationClient
Slog.e(TAG, "Remote exception", e);
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -492,7 +488,6 @@ public class FingerprintAuthenticationClient
Slog.e(TAG, "Remote exception", e);
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 36af5dba909f..fb48053913cf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -87,7 +87,6 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession>
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -107,7 +106,6 @@ public class FingerprintDetectClient extends AcquisitionClient<AidlSession>
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
mAuthenticationStateListeners.onAuthenticationStarted(
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 3a72d7e9a91a..993a68fd6ff8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -150,7 +150,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(
@@ -211,7 +210,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
);
super.onError(errorCode, vendorCode);
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -227,7 +225,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(),
getRequestReasonFromFingerprintEnrollReason(mEnrollReason), this);
mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo
@@ -277,7 +274,6 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT,
@@ -359,5 +355,14 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
}
@Override
+ public void setIgnoreDisplayTouches(boolean ignoreTouches) {
+ try {
+ getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send setIgnoreDisplayTouches", e);
+ }
+ }
+
+ @Override
public void onPowerPressed() {}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 1bddb83b1441..12baf00c1c4a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -790,6 +790,19 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) {
+ mFingerprintSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(
+ requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(),
+ "setIgnoreDisplayTouches received during client: " + client);
+ return;
+ }
+ ((Udfps) client).setIgnoreDisplayTouches(ignoreTouches);
+ });
+ }
+
+ @Override
public void onPowerPressed() {
for (int i = 0; i < mFingerprintSensors.size(); i++) {
final Sensor sensor = mFingerprintSensors.valueAt(i);
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 651828b6b9e2..7d26004b15c5 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -131,6 +131,25 @@ public class BrightnessSetting {
}
/**
+ * Sets the brightness. Does not send update event to listeners.
+ * @param brightness The value to which the brightness is to be set.
+ */
+ public void setBrightnessNoNotify(float brightness) {
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempting to init invalid brightness");
+ return;
+ }
+ synchronized (mSyncRoot) {
+ if (brightness != mBrightness) {
+ mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
+ brightness, mUserSerial
+ );
+ }
+ mBrightness = brightness;
+ }
+ }
+
+ /**
* @return The brightness for the default display in nits. Used when the underlying display
* device has changed but we want to persist the nit value.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2d5f38e2dd8c..670d59c09e06 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4782,6 +4782,8 @@ public final class DisplayManagerService extends SystemService {
}
final int[] supportedStates =
mDeviceStateManager.getSupportedStateIdentifiers();
+ // TODO(b/352019542): remove the log once b/345960547 is fixed.
+ Slog.d(TAG, "supportedStates=" + Arrays.toString(supportedStates));
DisplayInfo displayInfo;
for (int state : supportedStates) {
displayInfo = mLogicalDisplayMapper.getDisplayInfoForStateLocked(state,
@@ -4790,6 +4792,8 @@ public final class DisplayManagerService extends SystemService {
possibleInfo.add(displayInfo);
}
}
+ // TODO(b/352019542): remove the log once b/345960547 is fixed.
+ Slog.d(TAG, "possibleInfos=" + possibleInfo);
return possibleInfo;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 65a729a0293e..ae59d491b8f4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1565,7 +1565,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// even if they range changes what it means in absolute terms.
mDisplayBrightnessController.updateScreenBrightnessSetting(
MathUtils.constrain(unthrottledBrightnessState,
- clampedState.getMinBrightness(), clampedState.getMaxBrightness()));
+ clampedState.getMinBrightness(), clampedState.getMaxBrightness()),
+ Math.min(mBrightnessRangeController.getCurrentBrightnessMax(),
+ clampedState.getMaxBrightness()));
}
// The current brightness to use has been calculated at this point, and HbmController should
@@ -2455,12 +2457,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void setBrightness(float brightness) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness));
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness),
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
public void setBrightness(float brightness, int userSerial) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial);
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial,
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e645e98c215c..4791cd1403e2 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -389,11 +389,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// Retrieve the layout for this particular state.
final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
if (layout == null) {
+ // TODO(b/352019542): remove the log once b/345960547 is fixed.
+ Slog.d(TAG, "Cannot get layout for given state:" + deviceState);
return null;
}
// Retrieve the details of the given display within this layout.
Layout.Display display = layout.getById(displayId);
if (display == null) {
+ // TODO(b/352019542): remove the log once b/345960547 is fixed.
+ Slog.d(TAG, "Cannot get display for given layout:" + layout);
return null;
}
// Retrieve the display info for the display that matches the display id.
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 4982a0b0b836..c632e77fe685 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -313,13 +313,18 @@ public final class DisplayBrightnessController {
/**
* Notifies the brightnessSetting to persist the supplied brightness value.
*/
- public void setBrightness(float brightnessValue) {
+ public void setBrightness(float brightnessValue, float maxBrightness) {
// Update the setting, which will eventually call back into DPC to have us actually
// update the display with the new value.
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
- if (nits >= 0) {
+ float currentlyStoredNits = mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
+ // Don't override settings if the brightness is set to max, but the currently
+ // stored value is greater. On multi-screen device, when switching between a
+ // screen with a wider brightness range and one with a narrower brightness range,
+ // the stored value shouldn't change.
+ if (nits >= 0 && !(brightnessValue == maxBrightness && currentlyStoredNits > nits)) {
mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
}
}
@@ -328,15 +333,15 @@ public final class DisplayBrightnessController {
/**
* Notifies the brightnessSetting to persist the supplied brightness value for a user.
*/
- public void setBrightness(float brightnessValue, int userSerial) {
+ public void setBrightness(float brightnessValue, int userSerial, float maxBrightness) {
mBrightnessSetting.setUserSerial(userSerial);
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
* Sets the current screen brightness, and notifies the BrightnessSetting about the change.
*/
- public void updateScreenBrightnessSetting(float brightnessValue) {
+ public void updateScreenBrightnessSetting(float brightnessValue, float maxBrightness) {
synchronized (mLock) {
if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
|| brightnessValue == mCurrentScreenBrightness) {
@@ -345,7 +350,7 @@ public final class DisplayBrightnessController {
setCurrentScreenBrightnessLocked(brightnessValue);
}
notifyCurrentScreenBrightness();
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
@@ -582,7 +587,7 @@ public final class DisplayBrightnessController {
float brightnessForDefaultDisplay = getBrightnessFromNits(
brightnessNitsForDefaultDisplay);
if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) {
- mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
+ mBrightnessSetting.setBrightnessNoNotify(brightnessForDefaultDisplay);
currentBrightnessSetting = brightnessForDefaultDisplay;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 183b4b520a52..0f8060bba1ab 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3951,7 +3951,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason());
mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
- imeVisRes.getState(), imeVisRes.getReason());
+ imeVisRes.getState(), imeVisRes.getReason(), bindingController.mUserId);
if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) {
// If focused display changed, we should unbind current method
// to make app window in previous display relayout after Ime
@@ -4837,7 +4837,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@NonNull ImeVisibilityResult result) {
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(windowToken, statsToken, result.getState(),
- result.getReason());
+ result.getReason(), mCurrentUserId);
}
}
@@ -5121,7 +5121,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
if (imeVisRes != null) {
// Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker.
mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow,
- null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason());
+ null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason(),
+ mCurrentUserId);
}
// Eligible IME processes use new "setInteractive" protocol.
mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 363a4a7be3db..91a4d6f1707f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -1126,7 +1126,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
}
- private void sendHostEndpointConnectedEvent() {
+ void sendHostEndpointConnectedEvent() {
HostEndpointInfo info = new HostEndpointInfo();
info.hostEndpointId = (char) mHostEndPointId;
info.packageName = mPackage;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index b3fb147a318b..309dbed30d53 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -242,10 +242,14 @@ public class ContextHubService extends IContextHubService.Stub {
@Override
public void handleServiceRestart() {
- Log.i(TAG, "Starting Context Hub Service restart");
+ Log.i(TAG, "Recovering from Context Hub HAL restart...");
initExistingCallbacks();
resetSettings();
- Log.i(TAG, "Finished Context Hub Service restart");
+ if (Flags.reconnectHostEndpointsAfterHalRestart()) {
+ mClientManager.forEachClientOfHub(mContextHubId,
+ ContextHubClientBroker::sendHostEndpointConnectedEvent);
+ }
+ Log.i(TAG, "Finished recovering from Context Hub HAL restart");
}
@Override
@@ -536,6 +540,7 @@ public class ContextHubService extends IContextHubService.Stub {
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
try {
mContextHubWrapper.registerExistingCallback(contextHubId);
+ Log.i(TAG, "Re-registered callback to context hub " + contextHubId);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while registering existing service callback for hub "
+ "(ID = " + contextHubId + ")", e);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 3673eb027096..56b93e8ded82 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -839,6 +839,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
+ "Disallowed route: "
+ route);
}
+
+ if (route.isSystemRouteType()) {
+ throw new SecurityException(
+ "Only the system is allowed to publish routes with system route types. "
+ + "Disallowed route: "
+ + route);
+ }
}
Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 309e9450a01d..bc19b995bc14 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -189,9 +189,12 @@ public class PreferencesHelper implements RankingConfig {
int USER_LOCKED_BUBBLE = 0x00000002;
}
+ private final Object mLock = new Object();
// pkg|uid => PackagePreferences
+ @GuardedBy("mLock")
private final ArrayMap<String, PackagePreferences> mPackagePreferences = new ArrayMap<>();
// pkg|userId => PackagePreferences
+ @GuardedBy("mLock")
private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
private final Context mContext;
@@ -263,7 +266,7 @@ public class PreferencesHelper implements RankingConfig {
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
}
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
@@ -485,6 +488,7 @@ public class PreferencesHelper implements RankingConfig {
DEFAULT_BUBBLE_PREFERENCE, mClock.millis());
}
+ @GuardedBy("mLock")
private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
@UserIdInt int userId, int uid, int importance, int priority, int visibility,
boolean showBadge, int bubblePreference, long creationTime) {
@@ -616,7 +620,7 @@ public class PreferencesHelper implements RankingConfig {
notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId);
}
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
@@ -625,11 +629,10 @@ public class PreferencesHelper implements RankingConfig {
}
writePackageXml(r, out, notifPermissions, forBackup);
}
- }
- if (Flags.persistIncompleteRestoreData() && !forBackup) {
- synchronized (mRestoredWithoutUids) {
- final int N = mRestoredWithoutUids.size();
- for (int i = 0; i < N; i++) {
+
+ if (Flags.persistIncompleteRestoreData() && !forBackup) {
+ final int M = mRestoredWithoutUids.size();
+ for (int i = 0; i < M; i++) {
final PackagePreferences r = mRestoredWithoutUids.valueAt(i);
writePackageXml(r, out, notifPermissions, false);
}
@@ -732,7 +735,7 @@ public class PreferencesHelper implements RankingConfig {
*/
public void setBubblesAllowed(String pkg, int uid, int bubblePreference) {
boolean changed;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
changed = p.bubblePreference != bubblePreference;
p.bubblePreference = bubblePreference;
@@ -752,20 +755,20 @@ public class PreferencesHelper implements RankingConfig {
*/
@Override
public int getBubblePreference(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
return getOrCreatePackagePreferencesLocked(pkg, uid).bubblePreference;
}
}
public int getAppLockedFields(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
}
}
@Override
public boolean canShowBadge(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
}
}
@@ -773,7 +776,7 @@ public class PreferencesHelper implements RankingConfig {
@Override
public void setShowBadge(String packageName, int uid, boolean showBadge) {
boolean changed = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences pkgPrefs = getOrCreatePackagePreferencesLocked(packageName, uid);
if (pkgPrefs.showBadge != showBadge) {
pkgPrefs.showBadge = showBadge;
@@ -786,28 +789,28 @@ public class PreferencesHelper implements RankingConfig {
}
public boolean isInInvalidMsgState(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return r.hasSentInvalidMessage && !r.hasSentValidMessage;
}
}
public boolean hasUserDemotedInvalidMsgApp(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return isInInvalidMsgState(packageName, uid) ? r.userDemotedMsgApp : false;
}
}
public void setInvalidMsgAppDemoted(String packageName, int uid, boolean isDemoted) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
r.userDemotedMsgApp = isDemoted;
}
}
public boolean setInvalidMessageSent(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
boolean valueChanged = r.hasSentInvalidMessage == false;
r.hasSentInvalidMessage = true;
@@ -817,7 +820,7 @@ public class PreferencesHelper implements RankingConfig {
}
public boolean setValidMessageSent(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
boolean valueChanged = r.hasSentValidMessage == false;
r.hasSentValidMessage = true;
@@ -828,7 +831,7 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
boolean hasSentInvalidMsg(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return r.hasSentInvalidMessage;
}
@@ -836,7 +839,7 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
boolean hasSentValidMsg(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return r.hasSentValidMessage;
}
@@ -844,7 +847,7 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
boolean didUserEverDemoteInvalidMsgApp(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return r.userDemotedMsgApp;
}
@@ -852,7 +855,7 @@ public class PreferencesHelper implements RankingConfig {
/** Sets whether this package has sent a notification with valid bubble metadata. */
public boolean setValidBubbleSent(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
boolean valueChanged = !r.hasSentValidBubble;
r.hasSentValidBubble = true;
@@ -861,14 +864,14 @@ public class PreferencesHelper implements RankingConfig {
}
boolean hasSentValidBubble(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
return r.hasSentValidBubble;
}
}
boolean isImportanceLocked(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
return r.fixedImportance || r.defaultAppLockedImportance;
}
@@ -879,7 +882,7 @@ public class PreferencesHelper implements RankingConfig {
if (groupId == null) {
return false;
}
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
NotificationChannelGroup group = r.groups.get(groupId);
if (group == null) {
@@ -890,13 +893,13 @@ public class PreferencesHelper implements RankingConfig {
}
int getPackagePriority(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
}
}
int getPackageVisibility(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
}
}
@@ -911,7 +914,7 @@ public class PreferencesHelper implements RankingConfig {
throw new IllegalArgumentException("group.getName() can't be empty");
}
boolean needsDndChange = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
throw new IllegalArgumentException("Invalid package");
@@ -965,7 +968,7 @@ public class PreferencesHelper implements RankingConfig {
&& channel.getImportance() <= IMPORTANCE_MAX, "Invalid importance level");
boolean needsPolicyFileChange = false, wasUndeleted = false, needsDndChange = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
throw new IllegalArgumentException("Invalid package");
@@ -1109,7 +1112,7 @@ public class PreferencesHelper implements RankingConfig {
void unlockNotificationChannelImportance(String pkg, int uid, String updatedChannelId) {
Objects.requireNonNull(updatedChannelId);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
throw new IllegalArgumentException("Invalid package");
@@ -1131,7 +1134,7 @@ public class PreferencesHelper implements RankingConfig {
Objects.requireNonNull(updatedChannel.getId());
boolean changed = false;
boolean needsDndChange = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
throw new IllegalArgumentException("Invalid package");
@@ -1306,7 +1309,7 @@ public class PreferencesHelper implements RankingConfig {
String channelId, String conversationId, boolean returnParentIfNoConversationChannel,
boolean includeDeleted) {
Preconditions.checkNotNull(pkg);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
return null;
@@ -1347,7 +1350,7 @@ public class PreferencesHelper implements RankingConfig {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(conversationId);
List<NotificationChannel> channels = new ArrayList<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
return channels;
@@ -1367,7 +1370,7 @@ public class PreferencesHelper implements RankingConfig {
int callingUid, boolean fromSystemOrSystemUi) {
boolean deletedChannel = false;
boolean channelBypassedDnd = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return false;
@@ -1403,7 +1406,7 @@ public class PreferencesHelper implements RankingConfig {
public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
Objects.requireNonNull(pkg);
Objects.requireNonNull(channelId);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return;
@@ -1415,7 +1418,7 @@ public class PreferencesHelper implements RankingConfig {
@Override
public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
Objects.requireNonNull(pkg);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return;
@@ -1446,7 +1449,7 @@ public class PreferencesHelper implements RankingConfig {
boolean fixed = mPermissionHelper.isPermissionFixed(
pi.packageName, user.getUserHandle().getIdentifier());
if (fixed) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences p = getOrCreatePackagePreferencesLocked(
pi.packageName, pi.applicationInfo.uid);
p.fixedImportance = true;
@@ -1461,7 +1464,7 @@ public class PreferencesHelper implements RankingConfig {
public void updateDefaultApps(int userId, ArraySet<String> toRemove,
ArraySet<Pair<String, Integer>> toAdd) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
for (PackagePreferences p : mPackagePreferences.values()) {
if (userId == UserHandle.getUserId(p.uid)) {
if (toRemove != null && toRemove.contains(p.pkg)) {
@@ -1491,7 +1494,7 @@ public class PreferencesHelper implements RankingConfig {
public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
int uid, String groupId, boolean includeDeleted) {
Objects.requireNonNull(pkg);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
return null;
@@ -1514,7 +1517,7 @@ public class PreferencesHelper implements RankingConfig {
public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
int uid) {
Objects.requireNonNull(pkg);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return null;
@@ -1528,7 +1531,7 @@ public class PreferencesHelper implements RankingConfig {
boolean includeBlocked, Set<String> activeChannelFilter) {
Objects.requireNonNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
@@ -1575,7 +1578,7 @@ public class PreferencesHelper implements RankingConfig {
String groupId, int callingUid, boolean fromSystemOrSystemUi) {
List<NotificationChannel> deletedChannels = new ArrayList<>();
boolean groupBypassedDnd = false;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null || TextUtils.isEmpty(groupId)) {
return deletedChannels;
@@ -1607,7 +1610,7 @@ public class PreferencesHelper implements RankingConfig {
public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid) {
List<NotificationChannelGroup> groups = new ArrayList<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return groups;
@@ -1618,7 +1621,7 @@ public class PreferencesHelper implements RankingConfig {
}
public NotificationChannelGroup getGroupForChannel(String pkg, int uid, String channelId) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
if (p != null) {
NotificationChannel nc = p.channels.get(channelId);
@@ -1637,7 +1640,7 @@ public class PreferencesHelper implements RankingConfig {
public ArrayList<ConversationChannelWrapper> getConversations(IntArray userIds,
boolean onlyImportant) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
for (PackagePreferences p : mPackagePreferences.values()) {
if (userIds.binarySearch(UserHandle.getUserId(p.uid)) >= 0) {
@@ -1681,7 +1684,7 @@ public class PreferencesHelper implements RankingConfig {
public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
Objects.requireNonNull(pkg);
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return new ArrayList<>();
@@ -1723,7 +1726,7 @@ public class PreferencesHelper implements RankingConfig {
public @NonNull List<String> deleteConversations(String pkg, int uid,
Set<String> conversationIds, int callingUid, boolean fromSystemOrSystemUi) {
List<String> deletedChannelIds = new ArrayList<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return deletedChannelIds;
@@ -1756,7 +1759,7 @@ public class PreferencesHelper implements RankingConfig {
boolean includeDeleted) {
Objects.requireNonNull(pkg);
List<NotificationChannel> channels = new ArrayList<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
@@ -1778,7 +1781,7 @@ public class PreferencesHelper implements RankingConfig {
public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
int uid) {
List<NotificationChannel> channels = new ArrayList<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final PackagePreferences r = mPackagePreferences.get(
packagePreferencesKey(pkg, uid));
if (r != null) {
@@ -1799,7 +1802,7 @@ public class PreferencesHelper implements RankingConfig {
* upgrades.
*/
public boolean onlyHasDefaultChannel(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r.channels.size() == 1
&& r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
@@ -1812,7 +1815,7 @@ public class PreferencesHelper implements RankingConfig {
public int getDeletedChannelCount(String pkg, int uid) {
Objects.requireNonNull(pkg);
int deletedCount = 0;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return deletedCount;
@@ -1831,7 +1834,7 @@ public class PreferencesHelper implements RankingConfig {
public int getBlockedChannelCount(String pkg, int uid) {
Objects.requireNonNull(pkg);
int blockedCount = 0;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return blockedCount;
@@ -1874,7 +1877,7 @@ public class PreferencesHelper implements RankingConfig {
ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
final IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final int numPackagePreferences = mPackagePreferences.size();
for (int i = 0; i < numPackagePreferences; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
@@ -1943,7 +1946,7 @@ public class PreferencesHelper implements RankingConfig {
* considered for sentiment adjustments (and thus never show a blocking helper).
*/
public void setAppImportanceLocked(String packageName, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
return;
@@ -1959,7 +1962,7 @@ public class PreferencesHelper implements RankingConfig {
* Returns the delegate for a given package, if it's allowed by the package and the user.
*/
public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
if (prefs == null || prefs.delegate == null) {
@@ -1977,7 +1980,7 @@ public class PreferencesHelper implements RankingConfig {
*/
public void setNotificationDelegate(String sourcePkg, int sourceUid,
String delegatePkg, int delegateUid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
prefs.delegate = new Delegate(delegatePkg, delegateUid, true);
}
@@ -1987,7 +1990,7 @@ public class PreferencesHelper implements RankingConfig {
* Used by an app to turn off its notification delegate.
*/
public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
if (prefs != null && prefs.delegate != null) {
prefs.delegate.mEnabled = false;
@@ -2001,7 +2004,7 @@ public class PreferencesHelper implements RankingConfig {
*/
public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
String potentialDelegatePkg, int potentialDelegateUid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
@@ -2047,24 +2050,25 @@ public class PreferencesHelper implements RankingConfig {
pw.println("per-package config version: " + XML_VERSION);
pw.println("PackagePreferences:");
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences, pkgPermissions);
+ pw.println("Restored without uid:");
+ dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids, null);
}
- pw.println("Restored without uid:");
- dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids, null);
}
public void dump(ProtoOutputStream proto,
@NonNull NotificationManagerService.DumpFilter filter,
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
mPackagePreferences, pkgPermissions);
+ dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID,
+ filter, mRestoredWithoutUids, null);
}
- dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
- mRestoredWithoutUids, null);
}
+ @GuardedBy("mLock")
private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
ArrayMap<String, PackagePreferences> packagePreferences,
@@ -2249,7 +2253,7 @@ public class PreferencesHelper implements RankingConfig {
pkgsWithPermissionsToHandle = pkgPermissions.keySet();
}
int pulledEvents = 0;
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
for (int i = 0; i < mPackagePreferences.size(); i++) {
if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
break;
@@ -2329,7 +2333,7 @@ public class PreferencesHelper implements RankingConfig {
* {@link StatsEvent}.
*/
public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
int totalChannelsPulled = 0;
for (int i = 0; i < mPackagePreferences.size(); i++) {
if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
@@ -2365,7 +2369,7 @@ public class PreferencesHelper implements RankingConfig {
* {@link StatsEvent}.
*/
public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
int totalGroupsPulled = 0;
for (int i = 0; i < mPackagePreferences.size(); i++) {
if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
@@ -2394,10 +2398,12 @@ public class PreferencesHelper implements RankingConfig {
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject ranking = new JSONObject();
JSONArray PackagePreferencess = new JSONArray();
- try {
- ranking.put("noUid", mRestoredWithoutUids.size());
- } catch (JSONException e) {
- // pass
+ synchronized (mLock) {
+ try {
+ ranking.put("noUid", mRestoredWithoutUids.size());
+ } catch (JSONException e) {
+ // pass
+ }
}
// Track data that we've handled from the permissions-based list
@@ -2406,7 +2412,7 @@ public class PreferencesHelper implements RankingConfig {
pkgsWithPermissionsToHandle = pkgPermissions.keySet();
}
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
@@ -2512,7 +2518,7 @@ public class PreferencesHelper implements RankingConfig {
}
public Map<Integer, String> getPackageBans() {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final int N = mPackagePreferences.size();
ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
@@ -2571,7 +2577,7 @@ public class PreferencesHelper implements RankingConfig {
private Map<String, Integer> getPackageChannels() {
ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
for (int i = 0; i < mPackagePreferences.size(); i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
int channelCount = 0;
@@ -2587,7 +2593,7 @@ public class PreferencesHelper implements RankingConfig {
}
public void onUserRemoved(int userId) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
int N = mPackagePreferences.size();
for (int i = N - 1; i >= 0; i--) {
PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
@@ -2599,7 +2605,7 @@ public class PreferencesHelper implements RankingConfig {
}
protected void onLocaleChanged(Context context, int userId) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
@@ -2628,22 +2634,24 @@ public class PreferencesHelper implements RankingConfig {
for (int i = 0; i < size; i++) {
final String pkg = pkgList[i];
final int uid = uidList[i];
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
mPackagePreferences.remove(packagePreferencesKey(pkg, uid));
+ mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
}
- mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
updated = true;
}
} else {
for (String pkg : pkgList) {
- // Package install
- final PackagePreferences r =
- mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId));
- if (r != null) {
- try {
- r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
- mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
- synchronized (mPackagePreferences) {
+ try {
+ // Package install
+ int uid = mPm.getPackageUidAsUser(pkg, changeUserId);
+ PackagePermission p = null;
+ synchronized (mLock) {
+ final PackagePreferences r =
+ mRestoredWithoutUids.get(unrestoredPackageKey(pkg, changeUserId));
+ if (r != null) {
+ r.uid = uid;
+ mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
// Try to restore any unrestored sound resources
@@ -2665,32 +2673,29 @@ public class PreferencesHelper implements RankingConfig {
channel.setSound(restoredUri, channel.getAudioAttributes());
}
}
- }
- if (r.migrateToPm) {
- try {
- PackagePermission p = new PackagePermission(
+
+ if (r.migrateToPm) {
+ p = new PackagePermission(
r.pkg, UserHandle.getUserId(r.uid),
r.importance != IMPORTANCE_NONE,
hasUserConfiguredSettings(r));
- mPermissionHelper.setNotificationPermission(p);
- } catch (Exception e) {
- Slog.e(TAG, "could not migrate setting for " + r.pkg, e);
}
+ updated = true;
}
- updated = true;
- } catch (Exception e) {
- Slog.e(TAG, "could not restore " + r.pkg, e);
}
+ if (p != null) {
+ mPermissionHelper.setNotificationPermission(p);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "could not restore " + pkg, e);
}
// Package upgrade
try {
- synchronized (mPackagePreferences) {
- PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
- mPm.getPackageUidAsUser(pkg, changeUserId));
- if (fullPackagePreferences != null) {
- updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
- updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
- }
+ PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
+ mPm.getPackageUidAsUser(pkg, changeUserId));
+ if (fullPackagePreferences != null) {
+ updated |= createDefaultChannelIfNeededLocked(fullPackagePreferences);
+ updated |= deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
}
} catch (PackageManager.NameNotFoundException e) {
}
@@ -2704,7 +2709,7 @@ public class PreferencesHelper implements RankingConfig {
}
public void clearData(String pkg, int uid) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
if (p != null) {
p.channels = new ArrayMap<>();
@@ -2891,7 +2896,7 @@ public class PreferencesHelper implements RankingConfig {
}
public void unlockAllNotificationChannels() {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
final int numPackagePreferences = mPackagePreferences.size();
for (int i = 0; i < numPackagePreferences; i++) {
final PackagePreferences r = mPackagePreferences.valueAt(i);
@@ -2908,7 +2913,7 @@ public class PreferencesHelper implements RankingConfig {
PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL),
user.getUserHandle().getIdentifier());
for (PackageInfo pi : packages) {
- synchronized (mPackagePreferences) {
+ synchronized (mLock) {
PackagePreferences p = getOrCreatePackagePreferencesLocked(
pi.packageName, pi.applicationInfo.uid);
if (p.migrateToPm && p.uid != UNKNOWN_UID) {
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f59ae168bdc3..ee0159d722b1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2207,11 +2207,17 @@ public class ComputerEngine implements Computer {
if (PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
return true;
}
- if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ boolean permissionGranted = requireFullPermission ? hasPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ : (hasPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
+ if (!permissionGranted) {
+ if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
+ return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
+ }
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return permissionGranted;
}
/**
@@ -2227,6 +2233,24 @@ public class ComputerEngine implements Computer {
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean hasPermission(String permission, int uid) {
+ return mContext.checkPermission(permission, Process.INVALID_PID, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Since isolated process cannot hold permissions, we check the permissions on the owner app
+ * for known isolated_compute_app cases because they belong to the same package.
+ */
+ private boolean checkIsolatedOwnerHasPermission(int callingUid, boolean requireFullPermission) {
+ int ownerUid = getIsolatedOwner(callingUid);
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid);
+ }
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, ownerUid);
+ }
+
public final boolean isCallerSameApp(String packageName, int uid) {
return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 1bdc58691114..009e9b862b0f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2504,13 +2504,13 @@ final class InstallPackageHelper {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private void enableRestrictedSettings(String pkgName, int appId, int userId) {
+ private void setAccessRestrictedSettingsMode(String pkgName, int appId, int userId, int mode) {
final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class);
final int uid = UserHandle.getUid(userId, appId);
appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
uid,
pkgName,
- AppOpsManager.MODE_ERRORED);
+ mode);
}
/**
@@ -2888,8 +2888,21 @@ final class InstallPackageHelper {
mPm.notifyPackageChanged(packageName, request.getAppId());
}
- if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
- || !android.security.Flags.extendEcmToAllSettings()) {
+ // Set the OP_ACCESS_RESTRICTED_SETTINGS op, which is used by ECM (see {@link
+ // EnhancedConfirmationManager}) as a persistent state denoting whether an app is
+ // currently guarded by ECM, not guarded by ECM, or (in Android V+) that this should
+ // be decided later.
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ final int appId = request.getAppId();
+ mPm.mHandler.post(() -> {
+ for (int userId : firstUserIds) {
+ // MODE_DEFAULT means that the app's guardedness will be decided lazily
+ setAccessRestrictedSettingsMode(packageName, appId, userId,
+ AppOpsManager.MODE_DEFAULT);
+ }
+ });
+ } else {
// Apply restricted settings on potentially dangerous packages. Needs to happen
// after appOpsManager is notified of the new package
if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
@@ -2898,7 +2911,9 @@ final class InstallPackageHelper {
final int appId = request.getAppId();
mPm.mHandler.post(() -> {
for (int userId : firstUserIds) {
- enableRestrictedSettings(packageName, appId, userId);
+ // MODE_ERRORED means that the app is explicitly guarded
+ setAccessRestrictedSettingsMode(packageName, appId, userId,
+ AppOpsManager.MODE_ERRORED);
}
});
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 563cfa4d0ecd..023f7655c7a8 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -71,6 +71,7 @@ import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutChangeCallback;
import android.content.pm.IncrementalStatesInfo;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.LauncherActivityInfoInternal;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
@@ -1856,9 +1857,11 @@ public class LauncherAppsService extends SystemService {
private String getInstallerPackage(@NonNull String packageName, int callingUserId) {
String installerPackageName = null;
try {
- installerPackageName =
- mIPM.getInstallSourceInfo(packageName, callingUserId)
- .getInstallingPackageName();
+ InstallSourceInfo info = mIPM.getInstallSourceInfo(packageName, callingUserId);
+ if (info == null) {
+ return installerPackageName;
+ }
+ installerPackageName = info.getInstallingPackageName();
} catch (RemoteException re) {
Slog.e(TAG, "Couldn't find installer for " + packageName, re);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00e9d8d595f1..47a79a3c4051 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1060,7 +1060,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isInstallDpcPackagesPermissionGranted = (snapshot.checkUidPermission(
android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid)
== PackageManager.PERMISSION_GRANTED);
- final int targetPackageUid = snapshot.getPackageUid(packageName, 0, userId);
+ // Also query the package uid for archived packages, so that the user confirmation
+ // dialog can be displayed for updating archived apps.
+ final int targetPackageUid = snapshot.getPackageUid(packageName,
+ PackageManager.MATCH_ARCHIVED_PACKAGES, userId);
final boolean isUpdate = targetPackageUid != -1 || isApexSession();
final InstallSourceInfo existingInstallSourceInfo = isUpdate
? snapshot.getInstallSourceInfo(packageName, userId)
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 90d6adc4fa52..630fcb614eb3 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -185,7 +185,8 @@ final class ReconcilePackageUtils {
removeAppKeySetData = true;
}
- if (!installRequest.isInstallSystem() && !isSystemPackage && !isApex
+ if ((installRequest.getScanFlags() & SCAN_BOOTING) == 0
+ && !installRequest.isInstallSystem() && !isSystemPackage && !isApex
&& signingDetails != null
&& systemPackage != null && systemPackage.getSigningDetails() != null
&& systemPackage.getSigningDetails().checkCapability(
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index db94d0e8b0ba..695e58dad01c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -58,6 +58,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
+import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
@@ -322,6 +323,12 @@ public class UserManagerService extends IUserManager.Stub {
*/
private static final long PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000;
+ /**
+ * The time duration (in milliseconds) of the window length for the auto-lock message alarm
+ */
+ private static final long PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_ALARM_WINDOW_MS =
+ TimeUnit.SECONDS.toMillis(55);
+
// Tron counters
private static final String TRON_GUEST_CREATED = "users_guest_created";
private static final String TRON_USER_CREATED = "users_user_created";
@@ -552,8 +559,15 @@ public class UserManagerService extends IUserManager.Stub {
private KeyguardManager.KeyguardLockedStateListener mKeyguardLockedStateListener;
- /** Token to identify and remove already scheduled private space auto-lock messages */
- private static final Object PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN = new Object();
+ /**
+ * {@link android.app.AlarmManager.OnAlarmListener} to schedule an alarm to enable
+ * auto-locking private space after screen timeout
+ */
+ private PrivateSpaceAutoLockTimer mPrivateSpaceAutoLockTimer;
+
+ /** Tag representing the alarm manager timer for auto-locking private space */
+ private static final String PRIVATE_SPACE_AUTO_LOCK_TIMER_TAG = "PrivateSpaceAutoLockTimer";
+
/** Content observer to get callbacks for privte space autolock settings changes */
private final SettingsObserver mPrivateSpaceAutoLockSettingsObserver;
@@ -604,22 +618,28 @@ public class UserManagerService extends IUserManager.Stub {
public void onReceive(Context context, Intent intent) {
if (isAutoLockForPrivateSpaceEnabled()) {
if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
- Slog.d(LOG_TAG, "SCREEN_OFF broadcast received");
- maybeScheduleMessageToAutoLockPrivateSpace();
+ maybeScheduleAlarmToAutoLockPrivateSpace();
} else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
Slog.d(LOG_TAG, "SCREEN_ON broadcast received, "
- + "removing queued message to auto-lock private space");
- // Remove any queued messages since the device is interactive again
- mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
+ + "removing pending alarms to auto-lock private space");
+ // Remove any pending alarm since the device is interactive again
+ cancelPendingAutoLockAlarms();
}
}
}
};
+ private void cancelPendingAutoLockAlarms() {
+ final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ if (alarmManager != null && mPrivateSpaceAutoLockTimer != null) {
+ alarmManager.cancel(mPrivateSpaceAutoLockTimer);
+ }
+ }
+
@VisibleForTesting
- void maybeScheduleMessageToAutoLockPrivateSpace() {
+ void maybeScheduleAlarmToAutoLockPrivateSpace() {
// No action needed if auto-lock on inactivity not selected
- int privateSpaceAutoLockPreference =
+ final int privateSpaceAutoLockPreference =
Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART,
@@ -632,26 +652,65 @@ public class UserManagerService extends IUserManager.Stub {
}
int privateProfileUserId = getPrivateProfileUserId();
if (privateProfileUserId != UserHandle.USER_NULL) {
- scheduleMessageToAutoLockPrivateSpace(privateProfileUserId,
- PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN,
+ if (isQuietModeEnabled(privateProfileUserId)) {
+ Slogf.d(LOG_TAG, "Not scheduling auto-lock alarm for %d, "
+ + "quiet mode already enabled", privateProfileUserId);
+ return;
+ }
+ scheduleAlarmToAutoLockPrivateSpace(privateProfileUserId,
PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_TIMEOUT_MS);
}
}
@VisibleForTesting
- void scheduleMessageToAutoLockPrivateSpace(int userId, Object token,
- long delayInMillis) {
- Slog.i(LOG_TAG, "Scheduling auto-lock message");
- mHandler.postDelayed(() -> {
+ void scheduleAlarmToAutoLockPrivateSpace(int userId, long delayInMillis) {
+ final AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ if (alarmManager == null) {
+ Slog.e(LOG_TAG, "AlarmManager not available, cannot schedule auto-lock alarm");
+ return;
+ }
+ initPrivateSpaceAutoLockTimer(userId);
+ final long alarmWindowStartTime = SystemClock.elapsedRealtime() + delayInMillis;
+ alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ alarmWindowStartTime,
+ PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_ALARM_WINDOW_MS,
+ PRIVATE_SPACE_AUTO_LOCK_TIMER_TAG,
+ new HandlerExecutor(mHandler),
+ mPrivateSpaceAutoLockTimer);
+ }
+
+ private void initPrivateSpaceAutoLockTimer(int userId) {
+ cancelPendingAutoLockAlarms();
+ if (mPrivateSpaceAutoLockTimer == null
+ || mPrivateSpaceAutoLockTimer.getUserId() != userId) {
+ mPrivateSpaceAutoLockTimer = new PrivateSpaceAutoLockTimer(userId);
+ }
+ }
+
+ private class PrivateSpaceAutoLockTimer implements AlarmManager.OnAlarmListener {
+
+ private final int mUserId;
+
+ PrivateSpaceAutoLockTimer(int userId) {
+ mUserId = userId;
+ }
+
+ int getUserId() {
+ return mUserId;
+ }
+
+ @Override
+ public void onAlarm() {
final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
if (powerManager != null && !powerManager.isInteractive()) {
- Slog.i(LOG_TAG, "Auto-locking private space with user-id " + userId);
- setQuietModeEnabledAsync(userId, true,
+ Slog.i(LOG_TAG, "Auto-locking private space with user-id " + mUserId);
+ setQuietModeEnabledAsync(mUserId, true,
/* target */ null, mContext.getPackageName());
} else {
- Slog.i(LOG_TAG, "Device is interactive, skipping auto-lock");
+ Slog.i(LOG_TAG, "Device is interactive, skipping auto-lock for profile user "
+ + mUserId);
}
- }, token, delayInMillis);
+ }
}
@RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
@@ -697,7 +756,7 @@ public class UserManagerService extends IUserManager.Stub {
// Unregister device inactivity broadcasts
if (mIsDeviceInactivityBroadcastReceiverRegistered) {
Slog.i(LOG_TAG, "Removing device inactivity broadcast receivers");
- mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
+ cancelPendingAutoLockAlarms();
mContext.unregisterReceiver(mDeviceInactivityBroadcastReceiver);
mIsDeviceInactivityBroadcastReceiverRegistered = false;
}
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 57ea233c0a2b..5eda6deb0eb0 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -149,6 +149,13 @@ final class DefaultPermissionGrantPolicy {
CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
}
+ private static final Set<String> CALL_LOG_PERMISSIONS = new ArraySet<>();
+ static {
+ CALL_LOG_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
+ CALL_LOG_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
+ }
+
+
private static final Set<String> ALWAYS_LOCATION_PERMISSIONS = new ArraySet<>();
static {
ALWAYS_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
@@ -753,7 +760,7 @@ final class DefaultPermissionGrantPolicy {
String contactsProviderPackage =
getDefaultProviderAuthorityPackage(ContactsContract.AUTHORITY, userId);
grantSystemFixedPermissionsToSystemPackage(pm, contactsProviderPackage, userId,
- CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
+ CONTACTS_PERMISSIONS, PHONE_PERMISSIONS, CALL_LOG_PERMISSIONS);
grantPermissionsToSystemPackage(pm, contactsProviderPackage, userId, STORAGE_PERMISSIONS);
// Device provisioning
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
index ab22e3e3f94c..1003a8152b92 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
@@ -126,7 +126,7 @@ public class GnssPowerCalculator extends PowerCalculator {
long totalTime = 0;
double totalPower = 0;
for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
- long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType);
+ long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType) / 1000;
totalTime += timePerLevel;
totalPower += mAveragePowerPerSignalQuality[i] * timePerLevel;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8fc696556e29..72c7be34597e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.wallpaper;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
@@ -2209,7 +2210,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public ParcelFileDescriptor getWallpaperWithFeature(String callingPkg, String callingFeatureId,
IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId,
boolean getCropped) {
- final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
+ final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL)
+ || hasPermission(MANAGE_EXTERNAL_STORAGE);
if (!hasPrivilege) {
mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b6bdc326c802..40c94dd476ef 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -158,7 +158,6 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -1911,7 +1910,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
- mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t);
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
@@ -8678,7 +8677,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Nullable Rect getParentAppBoundsOverride() {
- return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride);
+ return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
}
/**
@@ -8863,7 +8862,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Rect screenResolvedBounds =
mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
- final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
+ final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
@@ -9272,7 +9271,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
+ final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
@@ -9313,7 +9312,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
: newParentConfiguration.windowConfiguration.getBounds();
final Rect containerAppBounds = useResolvedBounds
? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
- : mResolveConfigHint.mTmpParentAppBoundsOverride;
+ : mResolveConfigHint.mParentAppBoundsOverride;
final int requestedOrientation = getRequestedConfigurationOrientation();
final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 939babc4df41..c55a1003f402 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -256,7 +256,8 @@ public class AppTransitionController {
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController
.removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
- mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations();
+ mDisplayContent.mAtmService.mBackNavigationController
+ .clearBackAnimations(false /* cancel */);
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 1ce324f68569..14ae918129f7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -252,7 +252,8 @@ class BackNavigationController {
// skip if one of participant activity is translucent
backType = BackNavigationInfo.TYPE_CALLBACK;
} else if (prevActivities.size() > 0) {
- if (!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities)) {
+ if ((!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities))
+ && isAllActivitiesCreated(prevActivities)) {
// We have another Activity in the same currentTask to go to
final WindowContainer parent = currentActivity.getParent();
final boolean canCustomize = parent != null
@@ -549,6 +550,17 @@ class BackNavigationController {
return !prevActivities.isEmpty();
}
+ private static boolean isAllActivitiesCreated(
+ @NonNull ArrayList<ActivityRecord> prevActivities) {
+ for (int i = prevActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord check = prevActivities.get(i);
+ if (check.isState(ActivityRecord.State.INITIALIZING)) {
+ return false;
+ }
+ }
+ return !prevActivities.isEmpty();
+ }
+
boolean isMonitoringTransition() {
return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote();
}
@@ -739,7 +751,7 @@ class BackNavigationController {
mObserver.sendResult(null /* result */);
}
if (isMonitorAnimationOrTransition()) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
}
cancelPendingAnimation();
}
@@ -831,24 +843,23 @@ class BackNavigationController {
* Cleanup animation, this can either happen when legacy transition ready, or when the Shell
* transition finish.
*/
- void clearBackAnimations() {
- mAnimationHandler.clearBackAnimateTarget();
+ void clearBackAnimations(boolean cancel) {
+ mAnimationHandler.clearBackAnimateTarget(cancel);
mNavigationMonitor.stopMonitorTransition();
mWaitTransitionFinish = null;
}
/**
- * Called when a transition finished.
- * Handle the pending animation when the running transition finished.
+ * Handle the pending animation when the running transition finished, all the visibility change
+ * has applied so ready to start pending predictive back animation.
* @param targets The final animation targets derived in transition.
* @param finishedTransition The finished transition target.
*/
void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
@NonNull Transition finishedTransition) {
if (finishedTransition == mWaitTransitionFinish) {
- clearBackAnimations();
+ clearBackAnimations(false /* cancel */);
}
-
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
return;
}
@@ -982,7 +993,7 @@ class BackNavigationController {
mCloseAdaptor = createAdaptor(close, false, mSwitchType);
if (mCloseAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
@@ -1001,7 +1012,7 @@ class BackNavigationController {
mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open);
if (!mOpenAnimAdaptor.isValid()) {
Slog.w(TAG, "compose animations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
mOpenActivities = openingActivities;
@@ -1013,7 +1024,7 @@ class BackNavigationController {
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
if (close == null || open == null || open.length == 0 || open.length > 2) {
Slog.e(TAG, "reset animation with null target close: "
+ close + " open: " + Arrays.toString(open));
@@ -1102,7 +1113,19 @@ class BackNavigationController {
return false;
}
- void finishPresentAnimations() {
+ void finishPresentAnimations(boolean cancel) {
+ if (mOpenActivities != null) {
+ for (int i = mOpenActivities.length - 1; i >= 0; --i) {
+ final ActivityRecord resetActivity = mOpenActivities[i];
+ if (resetActivity.mDisplayContent.isFixedRotationLaunchingApp(resetActivity)) {
+ resetActivity.mDisplayContent
+ .continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
+ if (resetActivity.mLaunchTaskBehind) {
+ restoreLaunchBehind(resetActivity, cancel);
+ }
+ }
+ }
if (mCloseAdaptor != null) {
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
@@ -1111,15 +1134,6 @@ class BackNavigationController {
mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch);
mOpenAnimAdaptor = null;
}
-
- if (mOpenActivities != null) {
- for (int i = mOpenActivities.length - 1; i >= 0; --i) {
- final ActivityRecord resetActivity = mOpenActivities[i];
- if (resetActivity.mLaunchTaskBehind) {
- restoreLaunchBehind(resetActivity);
- }
- }
- }
}
void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
@@ -1130,10 +1144,10 @@ class BackNavigationController {
mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
}
- void clearBackAnimateTarget() {
+ void clearBackAnimateTarget(boolean cancel) {
if (mComposed) {
mComposed = false;
- finishPresentAnimations();
+ finishPresentAnimations(cancel);
}
mWaitTransition = false;
mStartingSurfaceTargetMatch = false;
@@ -1649,7 +1663,7 @@ class BackNavigationController {
return;
}
if (!triggerBack) {
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
} else {
mWaitTransition = true;
}
@@ -1732,25 +1746,23 @@ class BackNavigationController {
true /* notifyClients */);
}
- private static void restoreLaunchBehind(@NonNull ActivityRecord activity) {
+ private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel) {
if (!activity.isAttached()) {
// The activity was detached from hierarchy.
return;
}
-
- if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
- activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
- }
-
- // Restore the launch-behind state.
- activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
activity.mLaunchTaskBehind = false;
- // Ignore all change
- activity.mTransitionController.mSnapshotController
- .mActivitySnapshotController.clearOnBackPressedActivities();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
activity);
+ if (cancel) {
+ // Restore the launch-behind state
+ // TODO b/347168362 Change status directly during collecting for a transition.
+ activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
+ // Ignore all change
+ activity.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.clearOnBackPressedActivities();
+ }
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -1770,7 +1782,7 @@ class BackNavigationController {
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
mPendingAnimation = null;
}
return;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index f70d2a58e781..872b4e102565 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
@@ -657,7 +656,6 @@ class KeyguardController {
final boolean lastKeyguardGoingAway = mKeyguardGoingAway;
final ActivityRecord lastDismissKeyguardActivity = mDismissingKeyguardActivity;
- final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
mRequestDismissKeyguard = false;
mOccluded = false;
@@ -666,7 +664,6 @@ class KeyguardController {
mDismissingKeyguardActivity = null;
mTopTurnScreenOnActivity = null;
- boolean occludedByActivity = false;
final Task task = getRootTaskForControllingOccluding(display);
final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null;
if (top != null) {
@@ -712,7 +709,7 @@ class KeyguardController {
if (mTopTurnScreenOnActivity != null
&& !mService.mWindowManager.mPowerManager.isInteractive()
- && (mRequestDismissKeyguard || occludedByActivity)) {
+ && (mRequestDismissKeyguard || mOccluded)) {
controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 362d4efa0825..2aa7c0c0a401 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -20,6 +20,7 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
import static android.window.TaskConstants.TASK_CHILD_LAYER_LETTERBOX_BACKGROUND;
+import android.annotation.NonNull;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -209,16 +210,18 @@ public class Letterbox {
return false;
}
- public void applySurfaceChanges(SurfaceControl.Transaction t) {
+ /** Applies surface changes such as colour, window crop, position and input info. */
+ public void applySurfaceChanges(@NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
if (useFullWindowSurface()) {
- mFullWindowSurface.applySurfaceChanges(t);
+ mFullWindowSurface.applySurfaceChanges(t, inputT);
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
} else {
for (LetterboxSurface surface : mSurfaces) {
- surface.applySurfaceChanges(t);
+ surface.applySurfaceChanges(t, inputT);
}
mFullWindowSurface.remove();
@@ -418,7 +421,8 @@ public class Letterbox {
return Math.max(0, mLayoutFrameGlobal.height());
}
- public void applySurfaceChanges(SurfaceControl.Transaction t) {
+ public void applySurfaceChanges(@NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
@@ -446,7 +450,7 @@ public class Letterbox {
}
if (mSurface != null && mInputInterceptor != null) {
mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
- t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
+ inputT.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 14a0467b1d7b..e6f41ba4b496 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -785,16 +785,18 @@ final class LetterboxUiController {
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
- updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction());
+ updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
+ mActivityRecord.getPendingTransaction());
}
- void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, @NonNull Transaction t,
+ @NonNull Transaction inputT) {
if (shouldNotLayoutLetterbox(winHint)) {
return;
}
layoutLetterboxIfNeeded(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(t);
+ mLetterbox.applySurfaceChanges(t, inputT);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f5ab38f72b54..4a564157c46f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2163,6 +2163,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
// will be updated later after this is collected in transition.
rootTask.setBoundsUnchecked(taskFragment.getBounds());
+ // The exit-PIP activity resumes early for seamless transition. In certain
+ // scenarios, this introduces unintended addition to recents. To address this,
+ // we mark the root task for automatic removal from recents. This ensures that
+ // after the pinned activity reparents to its original task, the root task is
+ // automatically removed from the recents list.
+ rootTask.autoRemoveRecents = true;
// Move the last recents animation transaction from original task to the new one.
if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3b3eeb496ab7..0562cd979cb8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -827,7 +827,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (changed && noSystemOverlayPermission) {
if (mAlertWindowSurfaces.isEmpty()) {
cancelAlertWindowNotification();
- } else if (mAlertWindowNotification == null) {
+ } else if (mAlertWindowNotification == null && !isSatellitePointingUiPackage()) {
mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
if (mShowingAlertWindowNotificationAllowed) {
mAlertWindowNotification.post();
@@ -842,6 +842,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
+ // TODO b/349195999 - short term solution to not show the satellite pointing ui notification.
+ private boolean isSatellitePointingUiPackage() {
+ if (mPackageName == null || !mPackageName.equals(mService.mContext.getString(
+ com.android.internal.R.string.config_pointing_ui_package))) {
+ return false;
+ }
+ return ActivityTaskManagerService.checkPermission(
+ android.Manifest.permission.SATELLITE_COMMUNICATION, mPid, mUid) == PERMISSION_GRANTED;
+ }
+
void setShowingAlertWindowNotificationAllowed(boolean allowed) {
mShowingAlertWindowNotificationAllowed = allowed;
if (mAlertWindowNotification != null) {
@@ -897,6 +907,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
+ if (isSatellitePointingUiPackage()) {
+ pw.print(prefix); pw.println("mIsSatellitePointingUiPackage=true");
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ab72e3c42d85..7f6499f0f4b5 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2241,13 +2241,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
static class ConfigOverrideHint {
@Nullable DisplayInfo mTmpOverrideDisplayInfo;
@Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
- @Nullable Rect mTmpParentAppBoundsOverride;
+ @Nullable Rect mParentAppBoundsOverride;
int mTmpOverrideConfigOrientation;
boolean mUseOverrideInsetsForConfig;
void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
boolean isFixedRotationTransforming) {
- mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+ mParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
final Insets insets;
if (mUseOverrideInsetsForConfig && dc != null) {
// Insets are decoupled from configuration by default from V+, use legacy
@@ -2269,13 +2269,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
} else {
insets = Insets.NONE;
}
- mTmpParentAppBoundsOverride.inset(insets);
+ mParentAppBoundsOverride.inset(insets);
}
void resetTmpOverrides() {
mTmpOverrideDisplayInfo = null;
mTmpCompatInsets = null;
- mTmpParentAppBoundsOverride = null;
mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED;
}
}
@@ -2364,7 +2363,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final Rect containingAppBounds;
if (insideParentBounds) {
containingAppBounds = useOverrideInsetsForConfig
- ? overrideHint.mTmpParentAppBoundsOverride
+ ? overrideHint.mParentAppBoundsOverride
: parentConfig.windowConfiguration.getAppBounds();
} else {
// Restrict appBounds to display non-decor rather than parent because the
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 63ca46991c73..dcfe350b1c42 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -68,6 +68,8 @@ import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import android.annotation.IntDef;
@@ -1195,7 +1197,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
mController.mFinishingTransition = this;
-
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
@@ -1380,7 +1381,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// If the activity was just inserted to an invisible task, it will keep INITIALIZING
// state. Then no need to notify the callback to avoid clearing some states
// unexpectedly, e.g. launch-task-behind.
- if (ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING)) {
+ // However, skip dispatch to predictive back animation target, because it only set
+ // launch-task-behind to make the activity become visible.
+ if ((ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING))
+ && !ar.isAnimating(PARENTS, ANIMATION_TYPE_PREDICT_BACK)) {
mController.dispatchLegacyAppTransitionFinished(ar);
}
@@ -1690,7 +1694,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// ActivityRecord#canShowWindows() may reject to show its window. The visibility also
// needs to be updated for STATE_ABORT.
commitVisibleActivities(transaction);
- commitVisibleWallpapers();
+ commitVisibleWallpapers(transaction);
if (mTransactionCompletedListeners != null) {
for (int i = 0; i < mTransactionCompletedListeners.size(); i++) {
@@ -2121,7 +2125,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
/**
* Reset waitingToshow for all wallpapers, and commit the visibility of the visible ones
*/
- private void commitVisibleWallpapers() {
+ private void commitVisibleWallpapers(SurfaceControl.Transaction t) {
boolean showWallpaper = shouldWallpaperBeVisible();
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken();
@@ -2129,6 +2133,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
wallpaper.commitVisibility(showWallpaper);
}
+ if (showWallpaper && Flags.ensureWallpaperInTransitions()
+ && wallpaper.isVisibleRequested()
+ && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) {
+ // If on a rotation leash, we need to explicitly show the wallpaper surface
+ // because shell only gets the leash and we don't allow non-transition logic
+ // to touch the surfaces until the transition is over.
+ t.show(wallpaper.getSurfaceControl());
+ }
}
}
}
@@ -2541,9 +2553,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (wc.asWindowState() != null) continue;
final ChangeInfo changeInfo = changes.get(wc);
-
- // Reject no-ops
- if (!changeInfo.hasChanged()) {
+ // Reject no-ops, unless wallpaper
+ if (!changeInfo.hasChanged()
+ && (!Flags.ensureWallpaperInTransitions() || wc.asWallpaperToken() == null)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Rejecting as no-op: %s", wc);
continue;
@@ -2827,6 +2839,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Use parent rotation because shell doesn't know the surface is rotated.
endRotation = parent.getWindowConfiguration().getRotation();
}
+ } else if (isWallpaper(target) && Flags.ensureWallpaperInTransitions()
+ && target.getRelativeDisplayRotation() != 0
+ && !target.mTransitionController.useShellTransitionsRotation()) {
+ // If the wallpaper is "fixed-rotated", shell is unaware of this, so use the
+ // "as-if-not-rotating" bounds and rotation
+ change.setEndAbsBounds(parent.getBounds());
+ endRotation = parent.getWindowConfiguration().getRotation();
} else {
change.setEndAbsBounds(bounds);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3e43f5a2da66..86440ac3782d 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -764,10 +765,19 @@ class WallpaperController {
void collectTopWallpapers(Transition transition) {
if (mFindResults.hasTopShowWhenLockedWallpaper()) {
- transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ if (Flags.ensureWallpaperInTransitions()) {
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper.mToken);
+ } else {
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ }
+
}
if (mFindResults.hasTopHideWhenLockedWallpaper()) {
- transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ if (Flags.ensureWallpaperInTransitions()) {
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper.mToken);
+ } else {
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 9d1551c12216..b7f8505b4a65 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -32,6 +32,7 @@ import android.os.RemoteException;
import android.util.SparseArray;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import java.util.function.Consumer;
@@ -80,6 +81,20 @@ class WallpaperWindowToken extends WindowToken {
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}
+ @Override
+ public void prepareSurfaces() {
+ super.prepareSurfaces();
+
+ if (Flags.ensureWallpaperInTransitions()) {
+ // Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
+ // changes directly. In transitions the transition player will take care of applying the
+ // visibility change.
+ if (!mTransitionController.inTransition(this)) {
+ getSyncTransaction().setVisibility(mSurfaceControl, isVisible());
+ }
+ }
+ }
+
/**
* Controls whether this wallpaper shows underneath the keyguard or is hidden and only
* revealed once keyguard is dismissed.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 72109d34ec8a..edab5605ab8f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -610,10 +610,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
- final boolean shouldDeferTransitionReady = transition != null && !t.isEmpty()
- && (transition.isCollecting() || Flags.alwaysDeferTransitionWhenApplyWct());
- if (shouldDeferTransitionReady) {
- transition.deferTransitionReady();
+ boolean deferTransitionReady = false;
+ if (transition != null && !t.isEmpty()) {
+ if (transition.isCollecting()) {
+ deferTransitionReady = true;
+ transition.deferTransitionReady();
+ } else if (Flags.alwaysDeferTransitionWhenApplyWct()) {
+ Slog.w(TAG, "Transition is not collecting when applyTransaction."
+ + " transition=" + transition + " state=" + transition.getState());
+ transition = null;
+ }
}
try {
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
@@ -767,7 +773,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
} finally {
- if (shouldDeferTransitionReady) {
+ if (deferTransitionReady) {
transition.continueTransitionReady();
}
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6fd7aa0e4a78..9ecd49213237 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -63,6 +63,7 @@ import android.view.animation.AnimationUtils;
import com.android.internal.protolog.common.LogLevel;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -374,9 +375,13 @@ class WindowStateAnimator {
ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
mWin, new RuntimeException().fillInStackTrace());
destroySurface(t);
- // Don't hide wallpaper if we're deferring the surface destroy
- // because of a surface change.
- mWallpaperControllerLocked.hideWallpapers(mWin);
+ if (Flags.ensureWallpaperInTransitions()) {
+ if (mWallpaperControllerLocked.isWallpaperTarget(mWin)) {
+ mWin.requestUpdateWallpaperIfNeeded();
+ }
+ } else {
+ mWallpaperControllerLocked.hideWallpapers(mWin);
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
+ " surface " + mSurfaceController + " session " + mSession + ": "
@@ -431,7 +436,9 @@ class WindowStateAnimator {
if (!w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
- mWallpaperControllerLocked.hideWallpapers(w);
+ if (!w.mIsWallpaper || !Flags.ensureWallpaperInTransitions()) {
+ mWallpaperControllerLocked.hideWallpapers(w);
+ }
// If we are waiting for this window to handle an orientation change. If this window is
// really hidden (gone for layout), there is no point in still waiting for it.
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index bb0838db97b5..c558aae5248e 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1115,8 +1115,10 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) {
+ var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
val implicitPermissions = MutableIndexedSet<String>()
forEachPackageInAppId(appId) {
+ targetSdkVersion = targetSdkVersion.coerceAtMost(it.androidPackage!!.targetSdkVersion)
implicitPermissions += it.androidPackage!!.implicitPermissions
}
implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName ->
@@ -1153,7 +1155,10 @@ class AppIdPermissionPolicy : SchemePolicy() {
newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME)
}
}
- if (implicitPermissionName in RETAIN_IMPLICIT_FLAGS_PERMISSIONS) {
+ if (
+ targetSdkVersion >= Build.VERSION_CODES.M &&
+ implicitPermissionName in NO_IMPLICIT_FLAG_PERMISSIONS
+ ) {
newFlags = newFlags andInv PermissionFlags.IMPLICIT
} else {
newFlags = newFlags or PermissionFlags.IMPLICIT
@@ -1782,7 +1787,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
private const val PLATFORM_PACKAGE_NAME = "android"
// A set of permissions that we don't want to revoke when they are no longer implicit.
- private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS =
+ private val NO_IMPLICIT_FLAG_PERMISSIONS =
indexedSetOf(
Manifest.permission.ACCESS_MEDIA_LOCATION,
Manifest.permission.ACTIVITY_RECOGNITION,
diff --git a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
index 6b20ef1d5519..996daf5a5f68 100644
--- a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
@@ -47,9 +47,8 @@ inline fun AtomicFile.readWithReserveCopy(block: (FileInputStream) -> Unit) {
/** Write to actual file and reserve file. */
@Throws(IOException::class)
inline fun AtomicFile.writeWithReserveCopy(block: (FileOutputStream) -> Unit) {
- val reserveFile = File(baseFile.parentFile, baseFile.name + ".reservecopy")
- reserveFile.delete()
writeInlined(block)
+ val reserveFile = File(baseFile.parentFile, baseFile.name + ".reservecopy")
try {
FileInputStream(baseFile).use { inputStream ->
FileOutputStream(reserveFile).use { outputStream ->
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 88d3238861f5..d7936fece089 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -255,7 +256,8 @@ public final class DisplayBrightnessControllerTest {
@Test
public void setBrightnessSetsInBrightnessSetting() {
float brightnessValue = 0.3f;
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightnessValue);
verify(mBrightnessSetting).setBrightness(brightnessValue);
}
@@ -266,20 +268,24 @@ public final class DisplayBrightnessControllerTest {
// Sets the appropriate value when valid, and not equal to the current brightness
float brightnessValue = 0.3f;
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
assertEquals(mDisplayBrightnessController.getCurrentBrightness(), brightnessValue, 0.0f);
verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable);
verify(mBrightnessSetting).setBrightness(brightnessValue);
// Does nothing if the value is invalid
- mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
+ clearInvocations(mBrightnessSetting);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN, maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
// Does nothing if the value is same as the current brightness
brightnessValue = 0.2f;
mDisplayBrightnessController.setAndNotifyCurrentScreenBrightness(brightnessValue);
verify(mBrightnessChangeExecutor, times(2)).execute(mOnBrightnessChangeRunnable);
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
}
@@ -366,7 +372,7 @@ public final class DisplayBrightnessControllerTest {
when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting).setBrightness(brightness);
+ verify(mBrightnessSetting).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -378,7 +384,7 @@ public final class DisplayBrightnessControllerTest {
when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -395,15 +401,73 @@ public final class DisplayBrightnessControllerTest {
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
verifyZeroInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
@Test
+ public void testDoesNotSetBrightnessNits_settingMaxBrightnessAndStoredValueGreater() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting, never()).setBrightnessNitsForDefaultDisplay(anyFloat());
+ }
+
+ @Test
+ public void testSetsBrightnessNits_storedValueLower() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 100f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+ @Test
+ public void testSetsBrightnessNits_lowerThanMax() {
+ float brightnessValue = 0.60f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+
+ @Test
public void testChangeBrightnessNitsWhenUserChanges() {
float brightnessValue1 = 0.3f;
float nits1 = 200f;
+ int userSerial1 = 1;
float brightnessValue2 = 0.5f;
float nits2 = 300f;
+ int userSerial2 = 2;
+ float maxBrightness = 0.65f;
AutomaticBrightnessController automaticBrightnessController = mock(
AutomaticBrightnessController.class);
when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1);
@@ -417,13 +481,13 @@ public final class DisplayBrightnessControllerTest {
verify(automaticBrightnessStrategy)
.setAutomaticBrightnessController(automaticBrightnessController);
- mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(1);
+ mDisplayBrightnessController.setBrightness(brightnessValue1, userSerial1, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial1);
verify(mBrightnessSetting).setBrightness(brightnessValue1);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1);
- mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(2);
+ mDisplayBrightnessController.setBrightness(brightnessValue2, userSerial2, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial2);
verify(mBrightnessSetting).setBrightness(brightnessValue2);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 79f1574105ba..37d87c4e5d97 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -683,15 +683,14 @@ public final class UserManagerServiceTest {
UserInfo privateProfileUser =
mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null);
- Mockito.doNothing().when(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
- eq(privateProfileUser.getUserHandle().getIdentifier()), any(),
- anyLong());
+ Mockito.doNothing().when(mSpiedUms).scheduleAlarmToAutoLockPrivateSpace(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), anyLong());
- mSpiedUms.maybeScheduleMessageToAutoLockPrivateSpace();
+ mSpiedUms.maybeScheduleAlarmToAutoLockPrivateSpace();
- Mockito.verify(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
- eq(privateProfileUser.getUserHandle().getIdentifier()), any(), anyLong());
+ Mockito.verify(mSpiedUms).scheduleAlarmToAutoLockPrivateSpace(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), anyLong());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
index 6d56c417f789..60c3659202be 100644
--- a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
+++ b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
@@ -17,17 +17,25 @@ package com.android.server.autofill;
import static com.google.common.truth.Truth.assertThat;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.ArrayList;
-import java.util.List;
-
@RunWith(JUnit4.class)
public class RequestIdTest {
+ private static final int TEST_DATASET_SIZE = 300;
+ private static final int TEST_WRAP_SIZE = 50; // Number of request ids before wrap happens
+ private static final String TAG = "RequestIdTest";
+
List<Integer> datasetPrimaryNoWrap = new ArrayList<>();
List<Integer> datasetPrimaryWrap = new ArrayList<>();
List<Integer> datasetSecondaryNoWrap = new ArrayList<>();
@@ -35,151 +43,200 @@ public class RequestIdTest {
List<Integer> datasetMixedNoWrap = new ArrayList<>();
List<Integer> datasetMixedWrap = new ArrayList<>();
- @Before
- public void setup() throws Exception {
- int datasetSize = 300;
+ List<Integer> manualWrapRequestIdList = Arrays.asList(3, 9, 15,
+ RequestId.MAX_SECONDARY_REQUEST_ID - 5,
+ RequestId.MAX_SECONDARY_REQUEST_ID - 3);
+ List<Integer> manualNoWrapRequestIdList =Arrays.asList(2, 6, 10, 14, 18, 22, 26, 30);
+ List<Integer> manualOneElementRequestIdList = Arrays.asList(1);
+
+ @Before
+ public void setup() throws IllegalArgumentException {
+ Slog.d(TAG, "setup()");
{ // Generate primary only ids that do not wrap
- RequestId requestId = new RequestId(0);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MIN_PRIMARY_REQUEST_ID);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetPrimaryNoWrap.add(requestId.nextId(false));
}
+ Collections.sort(datasetPrimaryNoWrap);
}
{ // Generate primary only ids that wrap
- RequestId requestId = new RequestId(0xff00);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MAX_PRIMARY_REQUEST_ID -
+ TEST_WRAP_SIZE * 2);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetPrimaryWrap.add(requestId.nextId(false));
}
+ Collections.sort(datasetPrimaryWrap);
}
{ // Generate SECONDARY only ids that do not wrap
- RequestId requestId = new RequestId(0);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MIN_SECONDARY_REQUEST_ID);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetSecondaryNoWrap.add(requestId.nextId(true));
}
+ Collections.sort(datasetSecondaryNoWrap);
}
{ // Generate SECONDARY only ids that wrap
- RequestId requestId = new RequestId(0xff00);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MAX_SECONDARY_REQUEST_ID -
+ TEST_WRAP_SIZE * 2);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetSecondaryWrap.add(requestId.nextId(true));
}
+ Collections.sort(datasetSecondaryWrap);
}
{ // Generate MIXED only ids that do not wrap
- RequestId requestId = new RequestId(0);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MIN_REQUEST_ID);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetMixedNoWrap.add(requestId.nextId(i % 2 != 0));
}
+ Collections.sort(datasetMixedNoWrap);
}
{ // Generate MIXED only ids that wrap
- RequestId requestId = new RequestId(0xff00);
- for (int i = 0; i < datasetSize; i++) {
+ RequestId requestId = new RequestId(RequestId.MAX_REQUEST_ID -
+ TEST_WRAP_SIZE);
+ for (int i = 0; i < TEST_DATASET_SIZE; i++) {
datasetMixedWrap.add(requestId.nextId(i % 2 != 0));
}
+ Collections.sort(datasetMixedWrap);
}
+ Slog.d(TAG, "finishing setup()");
}
@Test
public void testRequestIdLists() {
+ Slog.d(TAG, "testRequestIdLists()");
for (int id : datasetPrimaryNoWrap) {
assertThat(RequestId.isSecondaryProvider(id)).isFalse();
- assertThat(id >= 0).isTrue();
- assertThat(id < 0xffff).isTrue();
+ assertThat(id).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID);
+ assertThat(id).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID);
}
for (int id : datasetPrimaryWrap) {
assertThat(RequestId.isSecondaryProvider(id)).isFalse();
- assertThat(id >= 0).isTrue();
- assertThat(id < 0xffff).isTrue();
+ assertThat(id).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID);
+ assertThat(id).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID);
}
for (int id : datasetSecondaryNoWrap) {
assertThat(RequestId.isSecondaryProvider(id)).isTrue();
- assertThat(id >= 0).isTrue();
- assertThat(id < 0xffff).isTrue();
+ assertThat(id).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID);
+ assertThat(id).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID);
}
for (int id : datasetSecondaryWrap) {
assertThat(RequestId.isSecondaryProvider(id)).isTrue();
- assertThat(id >= 0).isTrue();
- assertThat(id < 0xffff).isTrue();
+ assertThat(id).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID);
+ assertThat(id).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID);
}
}
@Test
- public void testRequestIdGeneration() {
- RequestId requestId = new RequestId(0);
+ public void testCreateNewRequestId() {
+ Slog.d(TAG, "testCreateNewRequestId()");
+ for (int i = 0; i < 100000; i++) {
+ RequestId requestId = new RequestId();
+ assertThat(requestId.getRequestId()).isAtLeast(RequestId.MIN_REQUEST_ID);
+ assertThat(requestId.getRequestId()).isAtMost(RequestId.MAX_START_ID);
+ }
+ }
+ @Test
+ public void testGetNextRequestId() throws IllegalArgumentException{
+ Slog.d(TAG, "testGetNextRequestId()");
+ RequestId requestId = new RequestId();
// Large Primary
for (int i = 0; i < 100000; i++) {
int y = requestId.nextId(false);
assertThat(RequestId.isSecondaryProvider(y)).isFalse();
- assertThat(y >= 0).isTrue();
- assertThat(y < 0xffff).isTrue();
+ assertThat(y).isAtLeast(RequestId.MIN_PRIMARY_REQUEST_ID);
+ assertThat(y).isAtMost(RequestId.MAX_PRIMARY_REQUEST_ID);
}
// Large Secondary
- requestId = new RequestId(0);
+ requestId = new RequestId();
for (int i = 0; i < 100000; i++) {
int y = requestId.nextId(true);
assertThat(RequestId.isSecondaryProvider(y)).isTrue();
- assertThat(y >= 0).isTrue();
- assertThat(y < 0xffff).isTrue();
+ assertThat(y).isAtLeast(RequestId.MIN_SECONDARY_REQUEST_ID);
+ assertThat(y).isAtMost(RequestId.MAX_SECONDARY_REQUEST_ID);
}
// Large Mixed
- requestId = new RequestId(0);
+ requestId = new RequestId();
for (int i = 0; i < 50000; i++) {
int y = requestId.nextId(i % 2 != 0);
- assertThat(RequestId.isSecondaryProvider(y)).isEqualTo(i % 2 == 0);
- assertThat(y >= 0).isTrue();
- assertThat(y < 0xffff).isTrue();
+ assertThat(y).isAtLeast(RequestId.MIN_REQUEST_ID);
+ assertThat(y).isAtMost(RequestId.MAX_REQUEST_ID);
}
}
@Test
public void testGetLastRequestId() {
- // In this test, request ids are generated FIFO, so the last entry is also the last
- // request
+ Slog.d(TAG, "testGetLastRequestId()");
- { // Primary no wrap
- int lastIdIndex = datasetPrimaryNoWrap.size() - 1;
- int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap);
- assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ { // Primary no wrap
+ int lastIdIndex = datasetPrimaryNoWrap.size() - 1;
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
}
- { // Primary wrap
- int lastIdIndex = datasetPrimaryWrap.size() - 1;
+ { // Primary wrap
+ // The last index would be the # of request ids left after wrap
+ // minus 1 (index starts at 0)
+ int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1;
int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryWrap);
- assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ assertThat(lastComputedIdIndex).isEqualTo(lastIdIndex);
}
- { // Secondary no wrap
+ { // Secondary no wrap
int lastIdIndex = datasetSecondaryNoWrap.size() - 1;
int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryNoWrap);
assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
}
- { // Secondary wrap
- int lastIdIndex = datasetSecondaryWrap.size() - 1;
+ { // Secondary wrap
+ int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1;
int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryWrap);
assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
}
- { // Mixed no wrap
+ { // Mixed no wrap
int lastIdIndex = datasetMixedNoWrap.size() - 1;
int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedNoWrap);
assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
}
- { // Mixed wrap
- int lastIdIndex = datasetMixedWrap.size() - 1;
+ { // Mixed wrap
+ int lastIdIndex = TEST_DATASET_SIZE - TEST_WRAP_SIZE - 1;
int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedWrap);
assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
}
+ { // Manual wrap
+ int lastIdIndex = 2; // [3, 9, 15,
+ // MAX_SECONDARY_REQUEST_ID - 5, MAX_SECONDARY_REQUEST_ID - 3]
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(manualWrapRequestIdList);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+ }
+
+ { // Manual no wrap
+ int lastIdIndex = manualNoWrapRequestIdList.size() - 1; // [2, 6, 10, 14,
+ // 18, 22, 26, 30]
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(manualNoWrapRequestIdList);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+
+ }
+
+ { // Manual one element
+ int lastIdIndex = 0; // [1]
+ int lastComputedIdIndex = RequestId.getLastRequestIdIndex(
+ manualOneElementRequestIdList);
+ assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index ecd799f44552..6ec888cd2e45 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -406,8 +406,6 @@ public class FingerprintAuthenticationClientTest {
mContextInjector.getValue().accept(opContext);
verify(mHal).onContextChanged(same(opContext));
- verify(mHal, times(2)).setIgnoreDisplayTouches(
- opContext.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
client.stopHalOperation();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d1880d2f3528..c95d19539486 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -180,6 +180,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
@SmallTest
@@ -480,6 +481,34 @@ public class PreferencesHelperTest extends UiServiceTestCase {
when(mPm.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
}
+ private static void testThreadSafety(Runnable operationToTest, int nThreads,
+ int nRunsPerThread) throws Exception {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch doneLatch = new CountDownLatch(nThreads);
+
+ for (int i = 0; i < nThreads; i++) {
+ Runnable threadRunnable = () -> {
+ try {
+ startLatch.await();
+ for (int j = 0; j < nRunsPerThread; j++) {
+ operationToTest.run();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ doneLatch.countDown();
+ }
+ };
+ new Thread(threadRunnable, "Test Thread #" + i).start();
+ }
+
+ // Ready set go
+ startLatch.countDown();
+
+ // Wait for all test threads to be done.
+ doneLatch.await();
+ }
+
@Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
// Setup package notifications.
@@ -6044,6 +6073,35 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
}
+ @Test
+ public void testRestoredWithoutUid_threadSafety() throws Exception {
+ when(mPm.getPackageUidAsUser(anyString(), anyInt())).thenReturn(UNKNOWN_UID);
+ when(mPm.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+ when(mClock.millis()).thenReturn(System.currentTimeMillis());
+ testThreadSafety(() -> {
+ String id = "id";
+ String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + Thread.currentThread()+ "\" show_badge=\"true\">\n"
+ + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ + "show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "<package name=\"" + PKG_P + "\" show_badge=\"true\">\n"
+ + "</package>\n"
+ + "</ranking>\n";
+
+ try {
+ loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // trigger a removal from the list
+ mXmlHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_P},
+ new int[]{UNKNOWN_UID});
+ }, 20, 50);
+ }
+
private static NotificationChannel cloneChannel(NotificationChannel original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index a39a1a8637df..3867c44696d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -30,6 +30,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -158,7 +159,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status to test translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
final ActivityRecord topActivity = topTask.getTopMostActivity();
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
@@ -169,7 +170,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status to test if previous task is translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
recordA.setOccludesParent(false);
backNavigationInfo = startBackNavigation();
@@ -180,7 +182,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
topActivity.setOccludesParent(true);
recordA.setOccludesParent(true);
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -188,7 +190,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(recordA).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -245,8 +247,9 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
// reset drawing status
+ testCase.recordBack.setState(STOPPED, "stopped");
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -255,7 +258,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test if top activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
// simulate translucent
testCase.recordFront.setOccludesParent(false);
@@ -266,7 +269,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test if bottom activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordBack.findMainWindow());
// simulate translucent
testCase.recordBack.setOccludesParent(false);
@@ -277,7 +280,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status, test canShowWhenLocked
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(testCase.recordBack).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -480,7 +483,10 @@ public class BackNavigationControllerTests extends WindowTestsBase {
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+
+ final WindowState window = topTask.getTopVisibleAppMainWindow();
+ makeWindowVisibleAndDrawn(window);
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -840,7 +846,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
toHomeBuilder.build();
verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface(
any(), any(), any(), any(), any(), any());
- animationHandler.clearBackAnimateTarget();
+ animationHandler.clearBackAnimateTarget(true);
openActivities.clear();
// Back to ACTIVITY and TASK have the same logic, just with different target.
@@ -937,6 +943,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
testCase.recordFront = record2;
testCase.windowBack = window1;
testCase.windowFront = window2;
+ record1.setState(STOPPED, "stopped");
return testCase;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index d2494ff3fc9a..fbc4c7b6bd34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -172,14 +172,14 @@ public class LetterboxTest {
@Test
public void testSurfaceOrigin_applied() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setPosition(mSurfaces.top, -1000, -2000);
}
@Test
public void testApplySurfaceChanges_setColor() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
@@ -187,7 +187,7 @@ public class LetterboxTest {
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@@ -195,7 +195,7 @@ public class LetterboxTest {
@Test
public void testNeedsApplySurfaceChanges_wallpaperBackgroundRequested() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setAlpha(mSurfaces.top, 1.0f);
assertFalse(mLetterbox.needsApplySurfaceChanges());
@@ -204,14 +204,14 @@ public class LetterboxTest {
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
}
@Test
public void testNeedsApplySurfaceChanges_setParentSurface() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
assertFalse(mLetterbox.needsApplySurfaceChanges());
@@ -220,14 +220,14 @@ public class LetterboxTest {
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
}
@Test
public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNull(mSurfaces.fullWindowSurface);
}
@@ -236,7 +236,7 @@ public class LetterboxTest {
public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNotNull(mSurfaces.fullWindowSurface);
}
@@ -245,7 +245,7 @@ public class LetterboxTest {
public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
mHasWallpaperBackground = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNotNull(mSurfaces.fullWindowSurface);
}
@@ -254,7 +254,7 @@ public class LetterboxTest {
public void testNotIntersectsOrFullyContains_cornersRounded() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertTrue(mLetterbox.notIntersectsOrFullyContains(new Rect(1, 2, 9, 9)));
}
@@ -262,14 +262,19 @@ public class LetterboxTest {
@Test
public void testSurfaceOrigin_changeCausesReapply() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
clearInvocations(mTransaction);
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setPosition(mSurfaces.top, 0, 0);
}
+ private void applySurfaceChanges() {
+ mLetterbox.applySurfaceChanges(/* syncTransaction */ mTransaction,
+ /* pendingTransaction */ mTransaction);
+ }
+
class SurfaceControlMocker implements Supplier<SurfaceControl.Builder> {
private SurfaceControl.Builder mLeftBuilder;
public SurfaceControl left;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index eb79118fe1c7..3078df026d8a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -392,6 +392,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask());
assertNotEquals(newPipTask, activity1.getTask());
assertFalse("Created PiP task must not be in recents", newPipTask.inRecents);
+ assertThat(newPipTask.autoRemoveRecents).isTrue();
+ assertThat(activity1.getTask().autoRemoveRecents).isFalse();
}
/**
@@ -427,6 +429,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
bounds.scale(0.5f);
task.setBounds(bounds);
assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertThat(task.autoRemoveRecents).isFalse();
}
/**
@@ -451,6 +454,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
+ assertThat(task.autoRemoveRecents).isFalse();
}
/**
@@ -480,6 +484,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
ensureTaskPlacement(fullscreenTask, secondActivity);
assertTrue(pinnedRootTask.inPinnedWindowingMode());
assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
+ assertThat(pinnedRootTask.autoRemoveRecents).isTrue();
+ assertThat(secondActivity.getTask().autoRemoveRecents).isFalse();
}
@Test
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 e01cea3d62f8..ef0aa9ef7666 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -42,6 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
@@ -222,6 +223,27 @@ public class TaskTests extends WindowTestsBase {
}
@Test
+ public void testReparentPinnedActivityBackToOriginalTask() {
+ final ActivityRecord activityMain = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task originalTask = activityMain.getTask();
+ final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build();
+ activityPip.setState(RESUMED, "test");
+ mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip,
+ null /* launchIntoPipHostActivity */, "test");
+ final Task pinnedActivityTask = activityPip.getTask();
+
+ // Simulate pinnedActivityTask unintentionally added to recent during top activity resume.
+ mAtm.getRecentTasks().getRawTasks().add(pinnedActivityTask);
+
+ // Reparent the activity back to its original task when exiting PIP mode.
+ pinnedActivityTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertThat(activityPip.getTask()).isEqualTo(originalTask);
+ assertThat(originalTask.autoRemoveRecents).isFalse();
+ assertThat(mAtm.getRecentTasks().getRawTasks()).containsExactly(originalTask);
+ }
+
+ @Test
public void testReparent_BetweenDisplays() {
// Create first task on primary display.
final Task rootTask1 = createTask(mDisplayContent);
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 2a359cd56d1b..6caed14bc31d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -371,6 +371,24 @@ public final class SatelliteManager {
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24;
+ /**
+ * Telephony framework needs to access the current location of the device to perform the
+ * request. However, location in the settings is disabled by users.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25;
+
+ /**
+ * Telephony framework needs to access the current location of the device to perform the
+ * request. However, Telephony fails to fetch the current location from location service.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26;
+
/** @hide */
@IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
SATELLITE_RESULT_SUCCESS,
@@ -397,7 +415,9 @@ public final class SatelliteManager {
SATELLITE_RESULT_REQUEST_IN_PROGRESS,
SATELLITE_RESULT_MODEM_BUSY,
SATELLITE_RESULT_ILLEGAL_STATE,
- SATELLITE_RESULT_MODEM_TIMEOUT
+ SATELLITE_RESULT_MODEM_TIMEOUT,
+ SATELLITE_RESULT_LOCATION_DISABLED,
+ SATELLITE_RESULT_LOCATION_NOT_AVAILABLE
})
@Retention(RetentionPolicy.SOURCE)
public @interface SatelliteResult {}