summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp16
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/system-current.txt17
-rw-r--r--core/java/android/app/SystemServiceRegistry.java17
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java5
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig1
-rw-r--r--core/java/android/app/supervision/ISupervisionManager.aidl25
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java57
-rw-r--r--core/java/android/app/supervision/flags.aconfig10
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorConfig.java73
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/os/Binder.java7
-rw-r--r--core/java/android/service/dreams/DreamOverlayService.java10
-rw-r--r--core/java/android/view/ViewGroup.java110
-rw-r--r--core/java/android/view/ViewRootImpl.java9
-rw-r--r--core/java/android/widget/Editor.java186
-rw-r--r--core/java/android/widget/TextView.java32
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig10
-rw-r--r--core/java/com/android/internal/accessibility/util/ShortcutUtils.java30
-rw-r--r--core/java/com/android/internal/protolog/TEST_MAPPING7
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/android/widget/TextViewContextMenuTest.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt98
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java46
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt11
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt45
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/OWNERS1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt157
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt189
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt103
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt129
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt2
-rw-r--r--services/Android.bp2
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java32
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java11
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java16
-rw-r--r--services/core/java/com/android/server/display/mode/Vote.java22
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java57
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java12
-rw-r--r--services/core/java/com/android/server/wm/TrustedOverlayHost.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java168
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java29
-rw-r--r--services/java/com/android/server/SystemServer.java13
-rw-r--r--services/supervision/Android.bp22
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java67
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java8
-rw-r--r--services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java136
143 files changed, 2687 insertions, 1202 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b4127c5660f7..3c5686bd6d13 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -26,6 +26,7 @@ aconfig_declarations_group {
"android.app.flags-aconfig-java",
"android.app.ondeviceintelligence-aconfig-java",
"android.app.smartspace.flags-aconfig-java",
+ "android.app.supervision.flags-aconfig-java",
"android.app.usage.flags-aconfig-java",
"android.app.wearable.flags-aconfig-java",
"android.appwidget.flags-aconfig-java",
@@ -1212,6 +1213,21 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Supervision
+aconfig_declarations {
+ name: "android.app.supervision.flags-aconfig",
+ exportable: true,
+ package: "android.app.supervision.flags",
+ container: "system",
+ srcs: ["core/java/android/app/supervision/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.supervision.flags-aconfig-java",
+ aconfig_declarations: "android.app.supervision.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// SurfaceFlinger
java_aconfig_library {
name: "surfaceflinger_flags_java_lib",
diff --git a/core/api/current.txt b/core/api/current.txt
index d8eac414cfeb..861be4079acc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7968,7 +7968,7 @@ package android.app.admin {
field public static final String PERMISSION_GRANT_POLICY = "permissionGrant";
field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
- field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String SECURITY_LOGGING_POLICY = "securityLogging";
+ field public static final String SECURITY_LOGGING_POLICY = "securityLogging";
field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d3d89123653..5413c6606bcb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -201,7 +201,7 @@ package android {
field public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
- field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
+ field public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
@@ -1296,7 +1296,7 @@ package android.app.admin {
}
public final class DevicePolicyIdentifiers {
- field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String AUDIT_LOGGING_POLICY = "auditLogging";
+ field public static final String AUDIT_LOGGING_POLICY = "auditLogging";
}
public class DevicePolicyKeyguardService extends android.app.Service {
@@ -1308,7 +1308,7 @@ package android.app.admin {
public class DevicePolicyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String);
- method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void clearAuditLogEventCallback();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void clearAuditLogEventCallback();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void finalizeWorkProfileProvisioning(@NonNull android.os.UserHandle, @Nullable android.accounts.Account);
@@ -1328,7 +1328,7 @@ package android.app.admin {
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
- method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
method public boolean isDeviceManaged();
method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE) public boolean isDevicePotentiallyStolen();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1344,8 +1344,8 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean);
- method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
@@ -3694,9 +3694,11 @@ package android.companion.virtual.sensor {
method public int getMinDelay();
method @NonNull public String getName();
method public float getPower();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public int getReportingMode();
method public float getResolution();
method public int getType();
method @Nullable public String getVendor();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
}
@@ -3710,8 +3712,10 @@ package android.companion.virtual.sensor {
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaximumRange(float);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMinDelay(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setPower(float);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setReportingMode(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setResolution(float);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setWakeUpSensor(boolean);
}
public interface VirtualSensorDirectChannelCallback {
@@ -8083,6 +8087,7 @@ package android.media.tv.tuner {
public class Tuner implements java.lang.AutoCloseable {
ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @Nullable String, int);
method public int applyFrontend(@NonNull android.media.tv.tuner.frontend.FrontendInfo);
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @RequiresPermission(allOf={"android.permission.TUNER_RESOURCE_ACCESS", "android.permission.ACCESS_TV_TUNER"}) public int applyFrontendByType(int);
method public int cancelScanning();
method public int cancelTuning();
method public void clearOnTuneEventListener();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index cb38cf297cf6..8b3ee24db025 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -48,6 +48,8 @@ import android.app.sdksandbox.SdkSandboxManagerFrameworkInitializer;
import android.app.search.SearchUiManager;
import android.app.slice.SliceManager;
import android.app.smartspace.SmartspaceManager;
+import android.app.supervision.ISupervisionManager;
+import android.app.supervision.SupervisionManager;
import android.app.time.TimeManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeDetectorImpl;
@@ -1703,6 +1705,21 @@ public final class SystemServiceRegistry {
return new E2eeContactKeysManager(ctx);
}});
+ registerService(Context.SUPERVISION_SERVICE, SupervisionManager.class,
+ new CachedServiceFetcher<>() {
+ @Override
+ public SupervisionManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ if (!android.app.supervision.flags.Flags.supervisionApi()) {
+ throw new ServiceNotFoundException(
+ "SupervisionManager is not supported");
+ }
+ IBinder iBinder = ServiceManager.getServiceOrThrow(
+ Context.SUPERVISION_SERVICE);
+ ISupervisionManager service = ISupervisionManager.Stub.asInterface(iBinder);
+ return new SupervisionManager(ctx, service);
+ }
+ });
// DO NOT do a flag check like this unless the flag is read-only.
// (because this code is executed during preload in zygote.)
// If the flag is mutable, the check should be inside CachedServiceFetcher.
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index eeaf0b3706fc..156512a90295 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -17,7 +17,6 @@
package android.app.admin;
import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
@@ -50,7 +49,6 @@ public final class DevicePolicyIdentifiers {
/**
* String identifier for {@link DevicePolicyManager#setSecurityLoggingEnabled}.
*/
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
public static final String SECURITY_LOGGING_POLICY = "securityLogging";
/**
@@ -58,7 +56,6 @@ public final class DevicePolicyIdentifiers {
*
* @hide
*/
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
@SystemApi
public static final String AUDIT_LOGGING_POLICY = "auditLogging";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ba1dc5677b21..5088ea6b603c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -60,7 +60,6 @@ import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
@@ -14335,7 +14334,6 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void setAuditLogEnabled(boolean enabled) {
throwIfParentInstance("setAuditLogEnabled");
@@ -14352,7 +14350,6 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public boolean isAuditLogEnabled() {
throwIfParentInstance("isAuditLogEnabled");
@@ -14374,7 +14371,6 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void setAuditLogEventCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -14401,7 +14397,6 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void clearAuditLogEventCallback() {
throwIfParentInstance("clearAuditLogEventCallback");
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 9148e3c3a072..56f47922b078 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -105,6 +105,7 @@ flag {
bug: "289520697"
}
+# Fully rolled out and must not be used.
flag {
name: "security_log_v2_enabled"
is_exported: true
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
new file mode 100644
index 000000000000..8d25cad2fc67
--- /dev/null
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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 android.app.supervision;
+
+/**
+ * Internal IPC interface to the supervision service.
+ * {@hide}
+ */
+interface ISupervisionManager {
+ boolean isSupervisionEnabled();
+}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
new file mode 100644
index 000000000000..8611a92074c0
--- /dev/null
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.app.supervision;
+
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * Service for handling parental supervision.
+ *
+ * @hide
+ */
+@SystemService(Context.SUPERVISION_SERVICE)
+public class SupervisionManager {
+ private final Context mContext;
+ private final ISupervisionManager mService;
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public SupervisionManager(Context context, ISupervisionManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Returns whether the device is supervised.
+ *
+ * @hide
+ */
+ public boolean isSupervisionEnabled() {
+ try {
+ return mService.isSupervisionEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+}
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
new file mode 100644
index 000000000000..bcb5b3636c95
--- /dev/null
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.app.supervision.flags"
+container: "system"
+
+flag {
+ name: "supervision_api"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag to enable the SupervisionService"
+ bug: "340351729"
+} \ No newline at end of file
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c3c3f0ef32e1..b4c36e1bc513 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -103,3 +103,10 @@ flag {
description: "Expose multiple surface for the virtual camera owner for different stream resolution"
bug: "341083465"
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "device_aware_display_power"
+ description: "Device awareness in power and display APIs"
+ bug: "285020111"
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 21ad914bbc29..82f183fd1d62 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -17,12 +17,14 @@
package android.companion.virtual.sensor;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.companion.virtualdevice.flags.Flags;
import android.hardware.Sensor;
import android.hardware.SensorDirectChannel;
import android.os.Parcel;
@@ -42,6 +44,13 @@ import java.util.Objects;
public final class VirtualSensorConfig implements Parcelable {
private static final String TAG = "VirtualSensorConfig";
+ // Defined in sensors.h
+ private static final int FLAG_WAKE_UP_SENSOR = 1;
+
+ // Mask for the reporting mode, bit 2, 3, 4.
+ private static final int REPORTING_MODE_MASK = 0xE;
+ private static final int REPORTING_MODE_SHIFT = 1;
+
// Mask for direct mode highest rate level, bit 7, 8, 9.
private static final int DIRECT_REPORT_MASK = 0x380;
private static final int DIRECT_REPORT_SHIFT = 7;
@@ -193,8 +202,7 @@ public final class VirtualSensorConfig implements Parcelable {
@SensorDirectChannel.RateLevel
public int getHighestDirectReportRateLevel() {
int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
- return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
- ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+ return Math.min(rateLevel, SensorDirectChannel.RATE_VERY_FAST);
}
/**
@@ -215,6 +223,28 @@ public final class VirtualSensorConfig implements Parcelable {
}
/**
+ * Returns whether the sensor is a wake-up sensor.
+ *
+ * @see Builder#setWakeUpSensor(boolean)
+ * @see Sensor#isWakeUpSensor()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public boolean isWakeUpSensor() {
+ return (mFlags & FLAG_WAKE_UP_SENSOR) > 0;
+ }
+
+ /**
+ * Returns the reporting mode of this sensor.
+ *
+ * @see Builder#setReportingMode(int)
+ * @see Sensor#getReportingMode()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public int getReportingMode() {
+ return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
+ }
+
+ /**
* Returns the sensor flags.
*
* @hide
@@ -383,6 +413,45 @@ public final class VirtualSensorConfig implements Parcelable {
}
return this;
}
+
+ /**
+ * Sets whether this sensor is a wake up sensor.
+ *
+ * @see Sensor#isWakeUpSensor()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public VirtualSensorConfig.Builder setWakeUpSensor(boolean wakeUpSensor) {
+ if (wakeUpSensor) {
+ mFlags |= FLAG_WAKE_UP_SENSOR;
+ } else {
+ mFlags &= ~FLAG_WAKE_UP_SENSOR;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the reporting mode of this sensor.
+ *
+ * @throws IllegalArgumentException if the reporting mode is not one of
+ * {@link Sensor#REPORTING_MODE_CONTINUOUS}, {@link Sensor#REPORTING_MODE_ON_CHANGE},
+ * {@link Sensor#REPORTING_MODE_ONE_SHOT}, or
+ * {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER}.
+ *
+ * @see Sensor#getReportingMode()
+ */
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public VirtualSensorConfig.Builder setReportingMode(int reportingMode) {
+ if (reportingMode != Sensor.REPORTING_MODE_CONTINUOUS
+ && reportingMode != Sensor.REPORTING_MODE_ON_CHANGE
+ && reportingMode != Sensor.REPORTING_MODE_ONE_SHOT
+ && reportingMode != Sensor.REPORTING_MODE_SPECIAL_TRIGGER) {
+ throw new IllegalArgumentException("Invalid reporting mode: " + reportingMode);
+ }
+ mFlags |= reportingMode << REPORTING_MODE_SHIFT;
+ return this;
+ }
}
@NonNull
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9c711bcb1521..3bf0f0324716 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6711,6 +6711,16 @@ public abstract class Context {
public static final String PROTOLOG_CONFIGURATION_SERVICE = "protolog_configuration";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.supervision.SupervisionManager}.
+ *
+ * @see #getSystemService(String)
+ * @see android.app.supervision.SupervisionManager
+ * @hide
+ */
+ public static final String SUPERVISION_SERVICE = "supervision";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b7556dfb51af..4bc3dbedeb94 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -708,9 +708,16 @@ public class Binder implements IBinder {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public final native void markVintfStability();
+ /** @hide */
+ private void markVintfStability$ravenwood() {
+ // This is not useful for Ravenwood which uses local binder.
+ // TODO(b/361785059): Use real native libbinder.
+ }
+
/**
* Use a VINTF-stability binder w/o VINTF requirements. Should be called
* on a binder before it is sent out of process.
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 013ec5f35761..244257cb61c8 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -51,6 +51,8 @@ public abstract class DreamOverlayService extends Service {
*/
private Executor mExecutor;
+ private boolean mCurrentRedirectToWake;
+
// An {@link IDreamOverlayClient} implementation that identifies itself when forwarding
// requests to the {@link DreamOverlayService}
private static class OverlayClient extends IDreamOverlayClient.Stub {
@@ -132,6 +134,10 @@ public abstract class DreamOverlayService extends Service {
mExecutor.execute(() -> {
endDreamInternal(mCurrentClient);
mCurrentClient = client;
+ if (Flags.dreamWakeRedirect()) {
+ mCurrentClient.redirectWake(mCurrentRedirectToWake);
+ }
+
onStartDream(params);
});
}
@@ -282,8 +288,10 @@ public abstract class DreamOverlayService extends Service {
return;
}
+ mCurrentRedirectToWake = redirect;
+
if (mCurrentClient == null) {
- throw new IllegalStateException("redirected wake with no dream present");
+ return;
}
mCurrentClient.redirectWake(redirect);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6f8838619808..3b5286a04b3d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3093,74 +3093,74 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
- final boolean handled;
-
- // Canceling motions is a special case. We don't need to perform any transformations
- // or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
- if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
- event.setAction(MotionEvent.ACTION_CANCEL);
- if (child == null) {
- handled = super.dispatchTouchEvent(event);
- } else {
- handled = child.dispatchTouchEvent(event);
+ try {
+ final boolean handled;
+ if (cancel) {
+ event.setAction(MotionEvent.ACTION_CANCEL);
}
- event.setAction(oldAction);
- return handled;
- }
- // Calculate the number of pointers to deliver.
- final int oldPointerIdBits = event.getPointerIdBits();
- final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
-
- // If for some reason we ended up in an inconsistent state where it looks like we
- // might produce a motion event with no pointers in it, then drop the event.
- if (newPointerIdBits == 0) {
- return false;
- }
+ // Calculate the number of pointers to deliver.
+ final int oldPointerIdBits = event.getPointerIdBits();
+ int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
- // If the number of pointers is the same and we don't need to perform any fancy
- // irreversible transformations, then we can reuse the motion event for this
- // dispatch as long as we are careful to revert any changes we make.
- // Otherwise we need to make a copy.
- final MotionEvent transformedEvent;
- if (newPointerIdBits == oldPointerIdBits) {
- if (child == null || child.hasIdentityMatrix()) {
- if (child == null) {
- handled = super.dispatchTouchEvent(event);
+ // If for some reason we ended up in an inconsistent state where it looks like we
+ // might produce a non-cancel motion event with no pointers in it, then drop the event.
+ // Make sure that we don't drop any cancel events.
+ if (newPointerIdBits == 0) {
+ if (event.getAction() != MotionEvent.ACTION_CANCEL) {
+ return false;
} else {
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- event.offsetLocation(offsetX, offsetY);
+ newPointerIdBits = oldPointerIdBits;
+ }
+ }
+
+ // If the number of pointers is the same and we don't need to perform any fancy
+ // irreversible transformations, then we can reuse the motion event for this
+ // dispatch as long as we are careful to revert any changes we make.
+ // Otherwise we need to make a copy.
+ final MotionEvent transformedEvent;
+ if (newPointerIdBits == oldPointerIdBits) {
+ if (child == null || child.hasIdentityMatrix()) {
+ if (child == null) {
+ handled = super.dispatchTouchEvent(event);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ event.offsetLocation(offsetX, offsetY);
- handled = child.dispatchTouchEvent(event);
+ handled = child.dispatchTouchEvent(event);
- event.offsetLocation(-offsetX, -offsetY);
+ event.offsetLocation(-offsetX, -offsetY);
+ }
+ return handled;
}
- return handled;
+ transformedEvent = MotionEvent.obtain(event);
+ } else {
+ transformedEvent = event.split(newPointerIdBits);
}
- transformedEvent = MotionEvent.obtain(event);
- } else {
- transformedEvent = event.split(newPointerIdBits);
- }
- // Perform any necessary transformations and dispatch.
- if (child == null) {
- handled = super.dispatchTouchEvent(transformedEvent);
- } else {
- final float offsetX = mScrollX - child.mLeft;
- final float offsetY = mScrollY - child.mTop;
- transformedEvent.offsetLocation(offsetX, offsetY);
- if (! child.hasIdentityMatrix()) {
- transformedEvent.transform(child.getInverseMatrix());
+ // Perform any necessary transformations and dispatch.
+ if (child == null) {
+ handled = super.dispatchTouchEvent(transformedEvent);
+ } else {
+ final float offsetX = mScrollX - child.mLeft;
+ final float offsetY = mScrollY - child.mTop;
+ transformedEvent.offsetLocation(offsetX, offsetY);
+ if (!child.hasIdentityMatrix()) {
+ transformedEvent.transform(child.getInverseMatrix());
+ }
+
+ handled = child.dispatchTouchEvent(transformedEvent);
}
- handled = child.dispatchTouchEvent(transformedEvent);
- }
+ // Done.
+ transformedEvent.recycle();
+ return handled;
- // Done.
- transformedEvent.recycle();
- return handled;
+ } finally {
+ event.setAction(oldAction);
+ }
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e0262715d2f..0e1625aaedd8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4388,14 +4388,7 @@ public final class ViewRootImpl implements ViewParent,
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
- if (mHasPendingTransactions) {
- // TODO: We shouldn't ever actually hit this, it means mPendingTransaction wasn't
- // merged with a sync group or BLASTBufferQueue before making it to this point
- // But better a one or two frame flicker than steady-state broken from dropping
- // whatever is in this transaction
- mPendingTransaction.apply();
- mHasPendingTransactions = false;
- }
+ mHasPendingTransactions = false;
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 03a26722da8f..0acc6bde5bfd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -20,6 +20,7 @@ import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+import static com.android.text.flags.Flags.contextMenuHideUnavailableItems;
import android.R;
import android.animation.ValueAnimator;
@@ -3250,62 +3251,135 @@ public class Editor {
final int menuItemOrderShare = 9;
final int menuItemOrderAutofill = 10;
- menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
- com.android.internal.R.string.undo)
- .setAlphabeticShortcut('z')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(0))
- .setEnabled(mTextView.canUndo());
- menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
- com.android.internal.R.string.redo)
- .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(1))
- .setEnabled(mTextView.canRedo());
-
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
- com.android.internal.R.string.cut)
- .setAlphabeticShortcut('x')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(2))
- .setEnabled(mTextView.canCut());
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
- com.android.internal.R.string.copy)
- .setAlphabeticShortcut('c')
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
- .setIcon(a.getDrawable(3))
- .setEnabled(mTextView.canCopy());
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
- com.android.internal.R.string.paste)
- .setAlphabeticShortcut('v')
- .setEnabled(mTextView.canPaste())
- .setIcon(a.getDrawable(4))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
- menuItemOrderPasteAsPlainText,
- com.android.internal.R.string.paste_as_plain_text)
- .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
- .setEnabled(mTextView.canPasteAsPlainText())
- .setIcon(a.getDrawable(4))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
- menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
- .setAlphabeticShortcut('a')
- .setEnabled(mTextView.canSelectAllText())
- .setIcon(a.getDrawable(5))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
-
- menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
- com.android.internal.R.string.share)
- .setEnabled(mTextView.canShare())
- .setIcon(a.getDrawable(6))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- final String selected = mTextView.getSelectedText();
- menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
- android.R.string.autofill)
- .setEnabled(mTextView.canRequestAutofill()
- && (selected == null || selected.isEmpty()))
- .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ if (contextMenuHideUnavailableItems()) {
+ if (mTextView.canUndo()) {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+ com.android.internal.R.string.undo)
+ .setAlphabeticShortcut('z')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(0));
+ }
+
+ if (mTextView.canRedo()) {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+ com.android.internal.R.string.redo)
+ .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(1));
+ }
+
+ if (mTextView.canCut()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+ com.android.internal.R.string.cut)
+ .setAlphabeticShortcut('x')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(2));
+ }
+
+ if (mTextView.canCopy()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+ com.android.internal.R.string.copy)
+ .setAlphabeticShortcut('c')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(3));
+ }
+
+ if (mTextView.canPaste()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+ com.android.internal.R.string.paste)
+ .setAlphabeticShortcut('v')
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canPasteAsPlainText()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+ menuItemOrderPasteAsPlainText,
+ com.android.internal.R.string.paste_as_plain_text)
+ .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canSelectAllText()) {
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+ menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+ .setAlphabeticShortcut('a')
+ .setIcon(a.getDrawable(5))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ if (mTextView.canShare()) {
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+ com.android.internal.R.string.share)
+ .setIcon(a.getDrawable(6))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+
+ final String selected = mTextView.getSelectedText();
+ if (mTextView.canRequestAutofill() && (selected == null || selected.isEmpty())) {
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+ android.R.string.autofill)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
+ } else {
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+ com.android.internal.R.string.undo)
+ .setAlphabeticShortcut('z')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(0))
+ .setEnabled(mTextView.canUndo());
+ menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+ com.android.internal.R.string.redo)
+ .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(1))
+ .setEnabled(mTextView.canRedo());
+
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+ com.android.internal.R.string.cut)
+ .setAlphabeticShortcut('x')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(2))
+ .setEnabled(mTextView.canCut());
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+ com.android.internal.R.string.copy)
+ .setAlphabeticShortcut('c')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setIcon(a.getDrawable(3))
+ .setEnabled(mTextView.canCopy());
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+ com.android.internal.R.string.paste)
+ .setAlphabeticShortcut('v')
+ .setEnabled(mTextView.canPaste())
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+ menuItemOrderPasteAsPlainText,
+ com.android.internal.R.string.paste_as_plain_text)
+ .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+ .setEnabled(mTextView.canPasteAsPlainText())
+ .setIcon(a.getDrawable(4))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+ menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+ .setAlphabeticShortcut('a')
+ .setEnabled(mTextView.canSelectAllText())
+ .setIcon(a.getDrawable(5))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+ com.android.internal.R.string.share)
+ .setEnabled(mTextView.canShare())
+ .setIcon(a.getDrawable(6))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ final String selected = mTextView.getSelectedText();
+ menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+ android.R.string.autofill)
+ .setEnabled(mTextView.canRequestAutofill()
+ && (selected == null || selected.isEmpty()))
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ }
a.recycle();
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1ea20fa85bd4..a346a679ea00 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -15552,15 +15552,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- boolean canUndo() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canUndo() {
return mEditor != null && mEditor.canUndo();
}
- boolean canRedo() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canRedo() {
return mEditor != null && mEditor.canRedo();
}
- boolean canCut() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canCut() {
if (hasPasswordTransformationMethod()) {
return false;
}
@@ -15573,7 +15579,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- boolean canCopy() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canCopy() {
if (hasPasswordTransformationMethod()) {
return false;
}
@@ -15594,7 +15602,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& isSuggestionsEnabled() && mEditor.shouldOfferToShowSuggestions();
}
- boolean canShare() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canShare() {
if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()
|| !getContext().getResources().getBoolean(
com.android.internal.R.bool.config_textShareSupported)) {
@@ -15613,8 +15623,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mDeviceProvisionedState == DEVICE_PROVISIONED_YES;
}
+ /** @hide */
+ @VisibleForTesting
@UnsupportedAppUsage
- boolean canPaste() {
+ public boolean canPaste() {
return (mText instanceof Editable
&& mEditor != null && mEditor.mKeyListener != null
&& getSelectionStart() >= 0
@@ -15622,7 +15634,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
&& getClipboardManagerForUser().hasPrimaryClip());
}
- boolean canPasteAsPlainText() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canPasteAsPlainText() {
if (!canPaste()) {
return false;
}
@@ -15644,7 +15658,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return canShare();
}
- boolean canSelectAllText() {
+ /** @hide */
+ @VisibleForTesting
+ public boolean canSelectAllText() {
return canSelectText() && !hasPasswordTransformationMethod()
&& !(getSelectionStart() == 0 && getSelectionEnd() == mText.length());
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 4f848175cd99..217bca77af0c 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -225,3 +225,13 @@ flag {
description: "Adds a minimize button the the caption bar"
bug: "356843241"
}
+
+flag {
+ name: "skip_compat_ui_education_in_desktop_mode"
+ namespace: "lse_desktop_experience"
+ description: "Ignore Compat UI educations when in Desktop Mode."
+ bug: "357062954"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 1c26687c97e9..2e0ff3db6c50 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -34,6 +34,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
@@ -176,24 +177,19 @@ public final class ShortcutUtils {
* @param type The shortcut type.
* @return Mapping key in Settings.
*/
+ @SuppressLint("SwitchIntDef")
public static String convertToKey(@UserShortcutType int type) {
- switch (type) {
- case SOFTWARE:
- return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
- case GESTURE:
- return Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
- case HARDWARE:
- return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
- case TRIPLETAP:
- return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
- case TWOFINGER_DOUBLETAP:
- return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
- case QUICK_SETTINGS:
- return Settings.Secure.ACCESSIBILITY_QS_TARGETS;
- default:
- throw new IllegalArgumentException(
- "Unsupported user shortcut type: " + type);
- }
+ return switch (type) {
+ case SOFTWARE -> Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+ case GESTURE -> Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
+ case HARDWARE -> Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+ case TRIPLETAP -> Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+ case TWOFINGER_DOUBLETAP ->
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
+ case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS;
+ default -> throw new IllegalArgumentException(
+ "Unsupported user shortcut type: " + type);
+ };
}
/**
diff --git a/core/java/com/android/internal/protolog/TEST_MAPPING b/core/java/com/android/internal/protolog/TEST_MAPPING
new file mode 100644
index 000000000000..37d57eed8cf4
--- /dev/null
+++ b/core/java/com/android/internal/protolog/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "ProtologPerfTests"
+ }
+ ]
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2dd560c69a8c..91c33702d3e3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3836,7 +3836,6 @@
<!-- Allows an application to use audit logging API.
@hide
@SystemApi
- @FlaggedApi("android.app.admin.flags.security_log_v2_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"
android:protectionLevel="internal|role" />
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index bcf1053e8ddd..3e76977c99fa 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -40,6 +41,9 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.textclassifier.TextClassification;
@@ -47,9 +51,12 @@ import android.view.textclassifier.TextClassification;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.verification.VerificationMode;
/**
* TextViewTest tests {@link TextView}.
@@ -86,6 +93,10 @@ public class TextViewContextMenuTest {
private SelectionActionModeHelper mMockHelper;
+ @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
+ new SetFlagsRule.ClassRule();
+ @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();
+
@Before
public void setUp() {
mMockHelper = mock(SelectionActionModeHelper.class);
@@ -234,6 +245,7 @@ public class TextViewContextMenuTest {
}
@Test
+ @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
public void testAutofillMenuItemEnabledWhenNoTextSelected() {
ContextMenu menu = mock(ContextMenu.class);
MenuItem mockMenuItem = newMockMenuItem();
@@ -254,6 +266,7 @@ public class TextViewContextMenuTest {
}
@Test
+ @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
public void testAutofillMenuItemNotEnabledWhenTextSelected() {
ContextMenu menu = mock(ContextMenu.class);
MenuItem mockMenuItem = newMockMenuItem();
@@ -271,4 +284,147 @@ public class TextViewContextMenuTest {
verify(menu).add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt());
verify(mockAutofillMenuItem).setEnabled(false);
}
+
+ private interface EditTextSetup {
+ void run(EditText et);
+ }
+
+ private void verifyMenuItemNotAdded(EditTextSetup setup, int id, VerificationMode times) {
+ ContextMenu menu = mock(ContextMenu.class);
+ MenuItem mockMenuItem = newMockMenuItem();
+ when(menu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mockMenuItem);
+ EditText et = spy(new EditText(getInstrumentation().getContext()));
+ setup.run(et);
+ Editor editor = new Editor(et);
+ editor.setTextContextMenuItems(menu);
+ verify(menu, times).add(anyInt(), eq(id), anyInt(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuUndoNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canUndo(),
+ TextView.ID_UNDO, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuUndoAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canUndo(), TextView.ID_UNDO,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuRedoNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRedo(), TextView.ID_REDO,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuRedoAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canRedo(), TextView.ID_REDO,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCutNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCut(), TextView.ID_CUT,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCutAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCut(), TextView.ID_CUT,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCopyNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCopy(), TextView.ID_COPY,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuCopyAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCopy(), TextView.ID_COPY,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPaste(), TextView.ID_PASTE,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPaste(), TextView.ID_PASTE,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAsPlaintextNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPasteAsPlainText(),
+ TextView.ID_PASTE_AS_PLAIN_TEXT, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuPasteAsPlaintextAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPasteAsPlainText(),
+ TextView.ID_PASTE_AS_PLAIN_TEXT, times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuSelectAllNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canSelectAllText(),
+ TextView.ID_SELECT_ALL, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuSelectAllAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canSelectAllText(),
+ TextView.ID_SELECT_ALL, times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuShareNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canShare(), TextView.ID_SHARE,
+ never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuShareAddedWhenAvailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canShare(), TextView.ID_SHARE,
+ times(1));
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuAutofillNotAddedWhenUnavailable() {
+ verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRequestAutofill(),
+ TextView.ID_AUTOFILL, never());
+ }
+
+ @Test
+ @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+ public void testContextMenuAutofillNotAddedWhenUnavailableBecauseTextSelected() {
+ verifyMenuItemNotAdded((spy) -> {
+ doReturn(true).when(spy).canRequestAutofill();
+ doReturn("test").when(spy).getSelectedText();
+ }, TextView.ID_AUTOFILL, never());
+ }
}
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 c2ee223b916a..972b78f6ca9a 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
@@ -39,6 +39,7 @@ import android.view.InsetsState;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
@@ -67,6 +68,7 @@ import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.IntPredicate;
import java.util.function.Predicate;
/**
@@ -189,6 +191,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
@NonNull
private final CompatUIStatusManager mCompatUIStatusManager;
+ @NonNull
+ private final IntPredicate mInDesktopModePredicate;
+
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -202,7 +207,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
@NonNull CompatUIConfiguration compatUIConfiguration,
@NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
@NonNull AccessibilityManager accessibilityManager,
- @NonNull CompatUIStatusManager compatUIStatusManager) {
+ @NonNull CompatUIStatusManager compatUIStatusManager,
+ @NonNull IntPredicate isDesktopModeEnablePredicate) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -218,6 +224,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis(
DISAPPEAR_DELAY_MS, flags);
mCompatUIStatusManager = compatUIStatusManager;
+ mInDesktopModePredicate = isDesktopModeEnablePredicate;
shellInit.addInitCallback(this::onInit, this);
}
@@ -251,7 +258,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
updateActiveTaskInfo(taskInfo);
}
- if (taskInfo.configuration == null || taskListener == null) {
+ // We close all the Compat UI educations in case we're in desktop mode.
+ if (taskInfo.configuration == null || taskListener == null
+ || isInDesktopMode(taskInfo.displayId)) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
return;
@@ -350,7 +359,6 @@ public class CompatUIController implements OnDisplaysChangedListener,
mOnInsetsChangedListeners.remove(displayId);
}
-
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
updateDisplayLayout(displayId);
@@ -692,7 +700,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
mContext.startActivityAsUser(intent, userHandle);
}
- private void removeLayouts(int taskId) {
+ @VisibleForTesting
+ void removeLayouts(int taskId) {
final CompatUIWindowManager compatLayout = mActiveCompatLayouts.get(taskId);
if (compatLayout != null) {
compatLayout.release();
@@ -825,4 +834,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
boolean mHasShownCameraCompatHint;
boolean mHasShownUserAspectRatioSettingsButtonHint;
}
+
+ private boolean isInDesktopMode(int displayId) {
+ return Flags.skipCompatUiEducationInDesktopMode()
+ && mInDesktopModePredicate.test(displayId);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 98536bf98f0b..42937c134e7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -137,6 +137,7 @@ import dagger.Module;
import dagger.Provides;
import java.util.Optional;
+import java.util.function.IntPredicate;
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
@@ -261,6 +262,7 @@ public abstract class WMShellBaseModule {
Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler,
Lazy<AccessibilityManager> accessibilityManager,
CompatUIRepository compatUIRepository,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@NonNull CompatUIState compatUIState,
@NonNull CompatUIComponentIdGenerator componentIdGenerator,
@NonNull CompatUIComponentFactory compatUIComponentFactory,
@@ -273,6 +275,10 @@ public abstract class WMShellBaseModule {
new DefaultCompatUIHandler(compatUIRepository, compatUIState,
componentIdGenerator, compatUIComponentFactory, mainExecutor));
}
+ final IntPredicate inDesktopModePredicate =
+ desktopModeTaskRepository.<IntPredicate>map(modeTaskRepository -> displayId ->
+ modeTaskRepository.getVisibleTaskCount(displayId) > 0)
+ .orElseGet(() -> displayId -> false);
return Optional.of(
new CompatUIController(
context,
@@ -288,7 +294,8 @@ public abstract class WMShellBaseModule {
compatUIConfiguration.get(),
compatUIShellCommandHandler.get(),
accessibilityManager.get(),
- compatUIStatusManager));
+ compatUIStatusManager,
+ inDesktopModePredicate));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
index a489c4ffdd94..423fe579a29a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger
+import android.os.Handler
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -24,22 +25,37 @@ import dagger.Provides
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
-/** Providers for various WmShell-specific coroutines-related constructs. */
+/**
+ * Providers for various WmShell-specific coroutines-related constructs.
+ *
+ * Providers of [MainCoroutineDispatcher] intentionally creates the dispatcher with a [Handler]
+ * backing it instead of a [ShellExecutor] because [ShellExecutor.asCoroutineDispatcher] will
+ * create a [CoroutineDispatcher] whose [CoroutineDispatcher.isDispatchNeeded] is effectively never
+ * dispatching. This is because even if dispatched, the backing [ShellExecutor.execute] always runs
+ * the [Runnable] immediately if called from the same thread, whereas
+ * [Handler.asCoroutineDispatcher] will create a [MainCoroutineDispatcher] that correctly
+ * dispatches (queues) when [CoroutineDispatcher.isDispatchNeeded] is true using [Handler.post].
+ * For callers that do need a non-dispatching version, [MainCoroutineDispatcher.immediate] is
+ * available.
+ */
@Module
class WMShellCoroutinesModule {
@Provides
@ShellMainThread
- fun provideMainDispatcher(@ShellMainThread mainExecutor: ShellExecutor): CoroutineDispatcher =
- mainExecutor.asCoroutineDispatcher()
+ fun provideMainDispatcher(
+ @ShellMainThread mainHandler: Handler
+ ): MainCoroutineDispatcher = mainHandler.asCoroutineDispatcher()
@Provides
@ShellBackgroundThread
fun provideBackgroundDispatcher(
- @ShellBackgroundThread backgroundExecutor: ShellExecutor
- ): CoroutineDispatcher = backgroundExecutor.asCoroutineDispatcher()
+ @ShellBackgroundThread backgroundHandler: Handler
+ ): MainCoroutineDispatcher = backgroundHandler.asCoroutineDispatcher()
@Provides
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 1a103d345ca7..d72ec90957fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -22,13 +22,15 @@ import android.graphics.Rect
import android.os.Bundle
import android.os.IBinder
import android.os.SystemClock
+import android.os.SystemProperties
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CLOSE
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
-import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
import com.android.internal.jank.InteractionJankMonitor
@@ -893,13 +895,10 @@ constructor(
) {
private val positionSpringConfig =
- PhysicsAnimator.SpringConfig(
- SpringForce.STIFFNESS_LOW,
- SpringForce.DAMPING_RATIO_LOW_BOUNCY
- )
+ PhysicsAnimator.SpringConfig(POSITION_SPRING_STIFFNESS, POSITION_SPRING_DAMPING_RATIO)
private val sizeSpringConfig =
- PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+ PhysicsAnimator.SpringConfig(SIZE_SPRING_STIFFNESS, SIZE_SPRING_DAMPING_RATIO)
/**
* @return layers in order:
@@ -929,7 +928,7 @@ constructor(
finishTransaction.hide(homeLeash)
// Setup freeform tasks before animation
state.freeformTaskChanges.forEach { change ->
- val startScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE
+ val startScale = FREEFORM_TASKS_INITIAL_SCALE
val startX =
change.endAbsBounds.left + change.endAbsBounds.width() * (1 - startScale) / 2
val startY =
@@ -994,9 +993,22 @@ constructor(
(animBounds.width() - startBounds.width()).toFloat() /
(endBounds.width() - startBounds.width())
val animScale = startScale + animFraction * (1 - startScale)
- // Freeform animation starts 50% in the animation
- val freeformAnimFraction = max(animFraction - 0.5f, 0f) * 2f
- val freeformStartScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE
+ // Freeform animation starts with freeform animation offset relative to the commit
+ // animation and plays until the commit animation ends. For instance:
+ // - if the freeform animation offset is `0.0` the freeform tasks animate alongside
+ // - if the freeform animation offset is `0.6` the freeform tasks will
+ // start animating at 60% fraction of the commit animation and will complete when
+ // the commit animation fraction is 100%.
+ // - if the freeform animation offset is `1.0` then freeform tasks will appear
+ // without animation after commit animation finishes.
+ val freeformAnimFraction =
+ if (FREEFORM_TASKS_ANIM_OFFSET != 1f) {
+ max(animFraction - FREEFORM_TASKS_ANIM_OFFSET, 0f) /
+ (1f - FREEFORM_TASKS_ANIM_OFFSET)
+ } else {
+ 0f
+ }
+ val freeformStartScale = FREEFORM_TASKS_INITIAL_SCALE
val freeformAnimScale =
freeformStartScale + freeformAnimFraction * (1 - freeformStartScale)
tx.apply {
@@ -1032,10 +1044,53 @@ constructor(
}
companion object {
+ /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */
+ private val FREEFORM_TASKS_INITIAL_SCALE =
+ propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f)
+
+ /** The freeform tasks animation offset relative to the whole animation duration. */
+ private val FREEFORM_TASKS_ANIM_OFFSET =
+ propertyValue("freeform_tasks_anim_offset", scale = 100f, default = 0.5f)
+
+ /** The spring force stiffness used to place the window into the final position. */
+ private val POSITION_SPRING_STIFFNESS =
+ propertyValue("position_stiffness", default = SpringForce.STIFFNESS_LOW)
+
+ /** The spring force damping ratio used to place the window into the final position. */
+ private val POSITION_SPRING_DAMPING_RATIO =
+ propertyValue(
+ "position_damping_ratio",
+ scale = 100f,
+ default = SpringForce.DAMPING_RATIO_LOW_BOUNCY
+ )
+
+ /** The spring force stiffness used to resize the window into the final bounds. */
+ private val SIZE_SPRING_STIFFNESS =
+ propertyValue("size_stiffness", default = SpringForce.STIFFNESS_LOW)
+
+ /** The spring force damping ratio used to resize the window into the final bounds. */
+ private val SIZE_SPRING_DAMPING_RATIO =
+ propertyValue(
+ "size_damping_ratio",
+ scale = 100f,
+ default = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ )
+
+ /** Drag to desktop transition system properties group. */
+ @VisibleForTesting
+ const val SYSTEM_PROPERTIES_GROUP = "persist.wm.debug.desktop_transitions.drag_to_desktop"
+
/**
- * The initial scale of the freeform tasks in the animation to commit the drag-to-desktop
- * gesture.
+ * Drag to desktop transition system property value with [name].
+ *
+ * @param scale an optional scale to apply to the value read from the system property.
+ * @param default a default value to return if the system property isn't set.
*/
- private const val DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE = 0.9f
+ @VisibleForTesting
+ fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float =
+ SystemProperties.getInt(
+ /* key= */ "$SYSTEM_PROPERTIES_GROUP.$name",
+ /* def= */ (default * scale).toInt()
+ ) / scale
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 84f6af4125b8..72d1a76b17e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -27,10 +27,13 @@ building to check the log state (is enabled) before printing the print format st
traces in Winscope)
### Kotlin
+Kotlin protologging is supported but not as optimized as in Java.
-Protolog tool does not yet have support for Kotlin code (see [b/168581922](https://b.corp.google.com/issues/168581922)).
-For logging in Kotlin, use the [KtProtoLog](/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt)
-class which has a similar API to the Java ProtoLog class.
+The Protolog tool does not yet have support for Kotlin code ([b/168581922](https://b.corp.google.com/issues/168581922)).
+
+What this implies is that ProtoLogs are not pre-processed to extract the static strings out when used in Kotlin. So,
+there is no memory gain when using ProtoLogging in Kotlin. The logs will still be traced to Perfetto, but with a subtly
+worse performance due to the additional string interning that needs to be done at run time instead of at build time.
### Enabling ProtoLog command line logging
Run these commands to enable protologs (in logcat) for WM Core ([list of all core tags](/core/java/com/android/internal/protolog/ProtoLogGroup.java)):
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 95f864a775be..8921ceb6175d 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
@@ -21,6 +21,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -684,6 +685,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
+ prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct);
wct.startTask(taskId1, options1);
startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
@@ -714,6 +716,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+ prepareTasksForSplitScreen(new int[] {taskId}, wct);
startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
@@ -757,11 +760,30 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ prepareTasksForSplitScreen(new int[] {taskId}, wct);
startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
}
/**
+ * Prepares the tasks whose IDs are provided in `taskIds` for split screen by clearing their
+ * bounds and windowing mode so that they can inherit the bounds and the windowing mode of
+ * their root stages.
+ *
+ * @param taskIds an array of task IDs whose bounds will be cleared.
+ * @param wct transaction to clear the bounds on the tasks.
+ */
+ private void prepareTasksForSplitScreen(int[] taskIds, WindowContainerTransaction wct) {
+ for (int taskId : taskIds) {
+ ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (task != null) {
+ wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+ .setBounds(task.token, null);
+ }
+ }
+ }
+
+ /**
* Starts with the second task to a split pair in one transition.
*
* @param wct transaction to start the first task
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index b39cf19a155a..d5287e742c2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -35,9 +35,12 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.res.Configuration;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -90,6 +93,9 @@ public class CompatUIControllerTest extends ShellTestCase {
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private CompatUIController mController;
private ShellInit mShellInit;
@Mock
@@ -122,7 +128,6 @@ public class CompatUIControllerTest extends ShellTestCase {
private CompatUIConfiguration mCompatUIConfiguration;
@Mock
private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
-
@Mock
private AccessibilityManager mAccessibilityManager;
@@ -132,6 +137,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@NonNull
private CompatUIStatusManager mCompatUIStatusManager;
+ private boolean mInDesktopModePredicateResult;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -157,7 +164,7 @@ public class CompatUIControllerTest extends ShellTestCase {
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager,
- mCompatUIStatusManager) {
+ mCompatUIStatusManager, i -> mInDesktopModePredicateResult) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -685,6 +692,7 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(false);
@@ -695,6 +703,34 @@ public class CompatUIControllerTest extends ShellTestCase {
eq(mMockTaskListener));
}
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
+ public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagEnabled() {
+ mInDesktopModePredicateResult = false;
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+ mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+ verify(mController, never()).removeLayouts(taskInfo.taskId);
+
+ mInDesktopModePredicateResult = true;
+ mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+ verify(mController).removeLayouts(taskInfo.taskId);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @DisableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
+ public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagDisabled() {
+ mInDesktopModePredicateResult = false;
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+ mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+ verify(mController, never()).removeLayouts(taskInfo.taskId);
+
+ mInDesktopModePredicateResult = true;
+ mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+ verify(mController, never()).removeLayouts(taskInfo.taskId);
+ }
+
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
/* isFocused */ false, /* isTopActivityTransparent */ false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 16a234b9e2f2..5b028371be2b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -8,6 +8,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WindowingMode
import android.graphics.PointF
import android.os.IBinder
+import android.os.SystemProperties
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
@@ -16,6 +17,7 @@ import android.window.TransitionInfo
import android.window.TransitionInfo.FLAG_IS_WALLPAPER
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -29,19 +31,24 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
import java.util.function.Supplier
+import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.MockitoSession
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyZeroInteractions
import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
/** Tests of [DragToDesktopTransitionHandler]. */
@SmallTest
@@ -61,10 +68,12 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
private lateinit var defaultHandler: DragToDesktopTransitionHandler
private lateinit var springHandler: SpringDragToDesktopTransitionHandler
+ private lateinit var mockitoSession: MockitoSession
@Before
fun setUp() {
- defaultHandler = DefaultDragToDesktopTransitionHandler(
+ defaultHandler =
+ DefaultDragToDesktopTransitionHandler(
context,
transitions,
taskDisplayAreaOrganizer,
@@ -72,7 +81,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
transactionSupplier,
)
.apply { setSplitScreenController(splitScreenController) }
- springHandler = SpringDragToDesktopTransitionHandler(
+ springHandler =
+ SpringDragToDesktopTransitionHandler(
context,
transitions,
taskDisplayAreaOrganizer,
@@ -80,6 +90,16 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
transactionSupplier,
)
.apply { setSplitScreenController(splitScreenController) }
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(SystemProperties::class.java)
+ .startMocking()
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
}
@Test
@@ -357,6 +377,77 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
verify(finishCallback).onTransitionFinished(null)
}
+ @Test
+ fun propertyValue_returnsSystemPropertyValue() {
+ val name = "property_name"
+ val value = 10f
+
+ whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt()))
+ .thenReturn(value.toInt())
+
+ assertEquals(
+ "Expects to return system properties stored value",
+ /* expected= */ value,
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name)
+ )
+ }
+
+ @Test
+ fun propertyValue_withScale_returnsScaledSystemPropertyValue() {
+ val name = "property_name"
+ val value = 10f
+ val scale = 100f
+
+ whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt()))
+ .thenReturn(value.toInt())
+
+ assertEquals(
+ "Expects to return scaled system properties stored value",
+ /* expected= */ value / scale,
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale)
+ )
+ }
+
+ @Test
+ fun propertyValue_notSet_returnsDefaultValue() {
+ val name = "property_name"
+ val defaultValue = 50f
+
+ whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(defaultValue.toInt())))
+ .thenReturn(defaultValue.toInt())
+
+ assertEquals(
+ "Expects to return the default value",
+ /* expected= */ defaultValue,
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
+ name,
+ default = defaultValue
+ )
+ )
+ }
+
+ @Test
+ fun propertyValue_withScaleNotSet_returnsDefaultValue() {
+ val name = "property_name"
+ val defaultValue = 0.5f
+ val scale = 100f
+ // Default value is multiplied when provided as a default value for [SystemProperties]
+ val scaledDefault = (defaultValue * scale).toInt()
+
+ whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(scaledDefault)))
+ .thenReturn(scaledDefault)
+
+ assertEquals(
+ "Expects to return the default value",
+ /* expected= */ defaultValue,
+ /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
+ name,
+ default = defaultValue,
+ scale = scale
+ )
+ )
+ }
+
private fun startDrag(
handler: DragToDesktopTransitionHandler,
task: RunningTaskInfo = createTask(),
@@ -462,4 +553,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
)
}
}
+
+ private fun systemPropertiesKey(name: String) =
+ "${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name"
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 2c71ee01b3f1..d14275ff2fd6 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -18,6 +18,7 @@ package android.media.tv.tuner;
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -32,6 +33,7 @@ import android.hardware.tv.tuner.Constant64Bit;
import android.hardware.tv.tuner.FrontendScanType;
import android.media.MediaCodec;
import android.media.tv.TvInputService;
+import android.media.tv.flags.Flags;
import android.media.tv.tuner.dvr.DvrPlayback;
import android.media.tv.tuner.dvr.DvrRecorder;
import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
@@ -2529,6 +2531,50 @@ public class Tuner implements AutoCloseable {
}
/**
+ * Request a frontend by frontend type.
+ *
+ * <p> This API is used if the applications want to select a frontend with desired type when
+ * there are multiple frontends of the same type is there before {@link tune}. The applied
+ * frontend will be one of the not in-use frontend. If all frontends are in-use, this API will
+ * reclaim and apply the frontend owned by the lowest priority client if current client has
+ * higher priority. Otherwise, this API will not apply any frontend and return
+ * {@link #RESULT_UNAVAILABLE}.
+ *
+ * @param desiredFrontendType the Type of the desired fronted. Should be one of
+ * {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
+ * @return result status of open operation.
+ */
+ @Result
+ @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+ @RequiresPermission(
+ allOf = {"android.permission.TUNER_RESOURCE_ACCESS", "android.permission.ACCESS_TV_TUNER"})
+ public int applyFrontendByType(@FrontendSettings.Type int desiredFrontendType) {
+ mFrontendLock.lock();
+ try {
+ if (mFeOwnerTuner != null) {
+ Log.e(TAG, "Operation connot be done by sharee of tuner");
+ return RESULT_INVALID_STATE;
+ }
+ if (mFrontendHandle != null) {
+ Log.e(TAG, "A frontend has been opened before");
+ return RESULT_INVALID_STATE;
+ }
+
+ mDesiredFrontendId = null;
+ mFrontendType = desiredFrontendType;
+ if (DEBUG) {
+ Log.d(TAG, "Applying frontend with type " + mFrontendType);
+ }
+ if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
+ return RESULT_UNAVAILABLE;
+ }
+ return RESULT_SUCCESS;
+ } finally {
+ mFrontendLock.unlock();
+ }
+ }
+
+ /**
* Open a shared filter instance.
*
* @param context the context of the caller.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS
new file mode 100644
index 000000000000..134a56ecb27e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS
@@ -0,0 +1 @@
+include /packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a129ac170d89..8a1d81be5e11 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1120,16 +1120,6 @@ flag {
}
flag {
- name: "glanceable_hub_back_gesture"
- namespace: "systemui"
- description: "Enables back gesture on the glanceable hub"
- bug: "346331399"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "glanceable_hub_allow_keyguard_when_dreaming"
namespace: "systemui"
description: "Allows users to exit dream to keyguard with glanceable hub enabled"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 872bef256f3a..ed1277666372 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -31,7 +31,6 @@ import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
@@ -47,7 +46,6 @@ import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.internal.R.attr.focusable
-import com.android.systemui.Flags.glanceableHubBackGesture
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -198,15 +196,7 @@ fun CommunalContainer(
Box(modifier = Modifier.fillMaxSize())
}
- val userActions =
- if (glanceableHubBackGesture()) {
- mapOf(
- Swipe(SwipeDirection.End) to CommunalScenes.Blank,
- Back to CommunalScenes.Blank,
- )
- } else {
- mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
- }
+ val userActions = mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
scene(CommunalScenes.Communal, userActions = userActions) {
CommunalScene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index d5874d1a7d3f..e17cb31407da 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.ui.composable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
@@ -28,6 +30,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -100,7 +103,13 @@ fun SceneContainer(
}
Box(
- modifier = Modifier.fillMaxSize(),
+ modifier =
+ Modifier.fillMaxSize().pointerInput(Unit) {
+ awaitEachGesture {
+ awaitFirstDown(false)
+ viewModel.onSceneContainerUserInputStarted()
+ }
+ },
) {
SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
sceneByKey.forEach { (sceneKey, composableScene) ->
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS
new file mode 100644
index 000000000000..f6f98e934dde
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index d850f17cd89a..65236f02b635 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -30,8 +30,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
-import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
@@ -90,8 +88,6 @@ class BouncerActionButtonInteractorTest : SysuiTestCase() {
.thenReturn(needsEmergencyAffordance)
whenever(telecomManager.isInCall).thenReturn(false)
- kosmos.fakeFeatureFlagsClassic.set(REFACTOR_GETCURRENTUSER, true)
-
kosmos.fakeTelephonyRepository.setHasTelephonyRadio(true)
kosmos.telecomManager = telecomManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c63381687f18..5b987b309b6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -175,10 +175,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
emergencyAffordanceManager = kosmos.emergencyAffordanceManager
whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
- kosmos.fakeFeatureFlagsClassic.apply {
- set(Flags.NEW_NETWORK_SLICE_UI, false)
- set(Flags.REFACTOR_GETCURRENTUSER, true)
- }
+ kosmos.fakeFeatureFlagsClassic.apply { set(Flags.NEW_NETWORK_SLICE_UI, false) }
mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
mobileConnectionsRepository.isAnySimSecure.value = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index e3a69a964b45..35cefa6b58df 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -401,10 +401,10 @@ class SceneInteractorTest : SysuiTestCase() {
underTest.setVisible(false, "reason")
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isFalse()
- underTest.onRemoteUserInteractionStarted("reason")
+ underTest.onRemoteUserInputStarted("reason")
assertThat(isVisible).isTrue()
- underTest.onUserInteractionFinished()
+ underTest.onUserInputFinished()
assertThat(isVisible).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index f856c559454c..832e7b1bcc0c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -237,7 +237,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
sceneInteractor.setVisible(false, "reason")
runCurrent()
assertThat(underTest.isVisible).isFalse()
- sceneInteractor.onRemoteUserInteractionStarted("reason")
+ sceneInteractor.onRemoteUserInputStarted("reason")
runCurrent()
assertThat(underTest.isVisible).isTrue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
index 6a886643cebb..8b97739af1db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -22,6 +22,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
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.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -83,4 +85,69 @@ class NotificationShadeWindowModelTest : SysuiTestCase() {
)
assertThat(isKeyguardOccluded).isFalse()
}
+
+ @Test
+ fun transitionFromOccludedToDreamingTransitionRemainsTrue() =
+ testScope.runTest {
+ val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
+ assertThat(isKeyguardOccluded).isFalse()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ),
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ value = 0.5f,
+ transitionState = TransitionState.RUNNING,
+ ),
+ ),
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isFalse()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ),
+ )
+ assertThat(isKeyguardOccluded).isTrue()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ),
+ TransitionStep(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ value = 0.5f,
+ transitionState = TransitionState.RUNNING,
+ ),
+ ),
+ testScope,
+ )
+ assertThat(isKeyguardOccluded).isTrue()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.OCCLUDED,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ),
+ )
+ assertThat(isKeyguardOccluded).isTrue()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
index 1356e93db549..06a2c5af2986 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
@@ -84,4 +85,48 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() {
)
)
}
+
+ @Test
+ fun shouldCloseGuts_userInputOngoing_currentGestureInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+ underTest.setCurrentGestureInGuts(true)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputOngoing_currentGestureNotInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+ underTest.setCurrentGestureInGuts(false)
+
+ assertThat(shouldCloseGuts).isTrue()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputNotOngoing_currentGestureInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onUserInputFinished()
+ underTest.setCurrentGestureInGuts(true)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputNotOngoing_currentGestureNotInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onUserInputFinished()
+ underTest.setCurrentGestureInGuts(false)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
}
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 0c11d2fa1d11..fc6d20e11d3b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -27,8 +27,6 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
- <bool name="config_use_large_screen_shade_header">true</bool>
-
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">2</integer>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index c594f1cd9313..b4383156dc71 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -35,6 +35,8 @@
<!-- How many lines to show in the security footer -->
<integer name="qs_security_footer_maxLines">1</integer>
+ <bool name="config_use_large_screen_shade_header">true</bool>
+
<!-- Whether to show bottom sheets edge to edge -->
<bool name="config_edgeToEdgeBottomSheetDialog">false</bool>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4ef1f93481f7..121577e438b0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -342,8 +342,7 @@ public class QuickStepContract {
// the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
|| (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0
- || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0
- || (sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+ || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) {
return false;
}
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 60fff282d041..9b45fa47cf21 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -483,22 +483,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@VisibleForTesting
SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
- private static int sCurrentUser;
-
- @Deprecated
- public synchronized static void setCurrentUser(int currentUser) {
- sCurrentUser = currentUser;
- }
-
- /**
- * @deprecated This can potentially return unexpected values in a multi user scenario
- * as this state is managed by another component. Consider using {@link SelectedUserInteractor}.
- */
- @Deprecated
- public synchronized static int getCurrentUser() {
- return sCurrentUser;
- }
-
@Override
public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
List<String> trustGrantedMessages) {
@@ -969,7 +953,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.removeCallbacks(mFpCancelNotReceived);
}
try {
- final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (userId != authUserId) {
mLogger.logFingerprintAuthForWrongUser(authUserId);
return;
@@ -1220,7 +1204,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.d("Aborted successful auth because device is going to sleep.");
return;
}
- final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (userId != authUserId) {
mLogger.logFaceAuthForWrongUser(authUserId);
return;
@@ -2462,7 +2446,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
- int user = mSelectedUserInteractor.getSelectedUserId(true);
+ int user = mSelectedUserInteractor.getSelectedUserId();
boolean isUserUnlocked = mUserManager.isUserUnlocked(user);
mLogger.logUserUnlockedInitialState(user, isUserUnlocked);
mUserIsUnlocked.put(user, isUserUnlocked);
@@ -4081,7 +4065,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
if (isFingerprintSupported()) {
- final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
@@ -4124,7 +4108,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintListenBuffer.toList()
).printTableData(pw);
} else if (mFpm != null && mFingerprintSensorProperties.isEmpty()) {
- final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
pw.println(" Fingerprint state (user=" + userId + ")");
pw.println(" mFingerprintSensorProperties.isEmpty="
+ mFingerprintSensorProperties.isEmpty());
@@ -4137,7 +4121,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintListenBuffer.toList()
).printTableData(pw);
}
- final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
new file mode 100644
index 000000000000..c94487848b81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
@@ -0,0 +1,67 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserTracker;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for tracking the current accessibility gesture list.
+ *
+ * @see Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS
+ */
+@MainThread
+@SysUISingleton
+public class AccessibilityGestureTargetsObserver extends
+ SecureSettingsContentObserver<AccessibilityGestureTargetsObserver.TargetsChangedListener> {
+
+ /** Listener for accessibility gesture targets changes. */
+ public interface TargetsChangedListener {
+
+ /**
+ * Called when accessibility gesture targets changes.
+ *
+ * @param targets Current content of {@link Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS}
+ */
+ void onAccessibilityGestureTargetsChanged(String targets);
+ }
+
+ @Inject
+ public AccessibilityGestureTargetsObserver(Context context, UserTracker userTracker) {
+ super(context, userTracker, Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
+ }
+
+ @Override
+ void onValueChanged(TargetsChangedListener listener, String value) {
+ listener.onAccessibilityGestureTargetsChanged(value);
+ }
+
+ /** Returns the current string from settings key
+ * {@link Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS}. */
+ @Nullable
+ public String getCurrentAccessibilityGestureTargets() {
+ return getSettingsValue();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 6757edba8ac3..b2d02edf3c45 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -120,7 +120,7 @@ constructor(
Intent.FLAG_ACTIVITY_NEW_TASK,
null,
activityOptions.toBundle(),
- selectedUserInteractor.getSelectedUserId(true),
+ selectedUserInteractor.getSelectedUserId(),
)
} catch (e: RemoteException) {
Log.w("CameraGestureHelper", "Unable to start camera activity", e)
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index a43447f7fcf4..aae21b97b163 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -66,7 +66,8 @@ public class EditTextActivity extends Activity
@Override
public WindowInsets onApplyWindowInsets(@NonNull View view,
@NonNull WindowInsets windowInsets) {
- Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+ Insets insets = windowInsets.getInsets(
+ WindowInsets.Type.systemBars() | WindowInsets.Type.ime());
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.leftMargin = insets.left;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index e07b5c228585..21922ff22afe 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -256,7 +256,7 @@ public class DozeSensors {
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable()
&& mConfig.alwaysOnEnabled(
- mSelectedUserInteractor.getSelectedUserId(true)),
+ mSelectedUserInteractor.getSelectedUserId()),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
false /* touchscreen */
@@ -297,7 +297,7 @@ public class DozeSensors {
private boolean udfpsLongPressConfigured() {
return mUdfpsEnrolled
- && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId(true))
+ && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId())
|| mScreenOffUdfpsEnabled);
}
@@ -477,7 +477,7 @@ public class DozeSensors {
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
- if (userId != mSelectedUserInteractor.getSelectedUserId(true)) {
+ if (userId != mSelectedUserInteractor.getSelectedUserId()) {
return;
}
for (TriggerSensor s : mTriggerSensors) {
@@ -703,13 +703,13 @@ public class DozeSensors {
}
protected boolean enabledBySetting() {
- if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId(true))) {
+ if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId())) {
return false;
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
- mSelectedUserInteractor.getSelectedUserId(true)) != 0;
+ mSelectedUserInteractor.getSelectedUserId()) != 0;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 4a9f741494f4..dd08d3262546 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -251,7 +251,7 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
- if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId(true))) {
+ if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId())) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4d75d661de49..bb73f569d945 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -55,19 +55,13 @@ object Flags {
// TODO(b/254512624): Tracking Bug
@JvmField
val NOTIFICATION_DRAG_TO_CONTENTS =
- resourceBooleanFlag(
- R.bool.config_notificationToContents,
- "notification_drag_to_contents"
- )
+ resourceBooleanFlag(R.bool.config_notificationToContents, "notification_drag_to_contents")
// TODO(b/280783617): Tracking Bug
@Keep
@JvmField
val BUILDER_EXTRAS_OVERRIDE =
- sysPropBooleanFlag(
- "persist.sysui.notification.builder_extras_override",
- default = true
- )
+ sysPropBooleanFlag("persist.sysui.notification.builder_extras_override", default = true)
// 200 - keyguard/lockscreen
// ** Flag retired **
@@ -81,10 +75,7 @@ object Flags {
// TODO(b/254512676): Tracking Bug
@JvmField
val LOCKSCREEN_CUSTOM_CLOCKS =
- resourceBooleanFlag(
- R.bool.config_enableLockScreenCustomClocks,
- "lockscreen_custom_clocks"
- )
+ resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks")
/**
* Whether the clock on a wide lock screen should use the new "stepping" animation for moving
@@ -99,10 +90,6 @@ object Flags {
// TODO(b/255607168): Tracking Bug
@JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
- // TODO(b/305984787):
- @JvmField
- val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
-
/** Flag to control the revamp of keyguard biometrics progress animation */
// TODO(b/244313043): Tracking bug
@JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
@@ -125,13 +112,11 @@ object Flags {
/** Whether the long-press gesture to open wallpaper picker is enabled. */
// TODO(b/266242192): Tracking Bug
- @JvmField
- val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
+ @JvmField val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
/** Inflate and bind views upon emitting a blueprint value . */
// TODO(b/297365780): Tracking Bug
- @JvmField
- val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
+ @JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
/** Enables UI updates for AI wallpapers in the wallpaper picker. */
// TODO(b/267722622): Tracking Bug
@@ -145,8 +130,7 @@ object Flags {
/** Add "Apply" button to wall paper picker's grid preview page. */
// TODO(b/294866904): Tracking bug.
@JvmField
- val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
- unreleasedFlag("wallpaper_picker_grid_apply_button")
+ val WALLPAPER_PICKER_GRID_APPLY_BUTTON = unreleasedFlag("wallpaper_picker_grid_apply_button")
/** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
// TODO(b/286563884): Tracking bug
@@ -190,10 +174,7 @@ object Flags {
// TODO(b/254512383): Tracking Bug
@JvmField
val FULL_SCREEN_USER_SWITCHER =
- resourceBooleanFlag(
- R.bool.config_enableFullscreenUserSwitcher,
- "full_screen_user_switcher"
- )
+ resourceBooleanFlag(R.bool.config_enableFullscreenUserSwitcher, "full_screen_user_switcher")
// TODO(b/244064524): Tracking Bug
@JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag("qs_secondary_data_sub_info")
@@ -212,16 +193,15 @@ object Flags {
@JvmField val NEW_NETWORK_SLICE_UI = releasedFlag("new_network_slice_ui")
// TODO(b/311222557): Tracking bug
- val ROAMING_INDICATOR_VIA_DISPLAY_INFO =
- releasedFlag("roaming_indicator_via_display_info")
+ val ROAMING_INDICATOR_VIA_DISPLAY_INFO = releasedFlag("roaming_indicator_via_display_info")
// TODO(b/308138154): Tracking bug
val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
releasedFlag("filter_provisioning_network_subscriptions")
// TODO(b/293863612): Tracking Bug
- @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
- releasedFlag("incompatible_charging_battery_icon")
+ @JvmField
+ val INCOMPATIBLE_CHARGING_BATTERY_ICON = releasedFlag("incompatible_charging_battery_icon")
// TODO(b/293585143): Tracking Bug
val INSTANT_TETHER = releasedFlag("instant_tether")
@@ -230,8 +210,7 @@ object Flags {
val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
// TODO(b/290676905): Tracking Bug
- val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
- releasedFlag("new_shade_carrier_group_mobile_icons")
+ val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS = releasedFlag("new_shade_carrier_group_mobile_icons")
// 800 - general visual/theme
@JvmField val MONET = resourceBooleanFlag(R.bool.flag_monet, "monet")
@@ -280,8 +259,7 @@ object Flags {
// TODO(b/273509374): Tracking Bug
@JvmField
- val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS =
- releasedFlag("always_show_home_controls_on_dreams")
+ val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = releasedFlag("always_show_home_controls_on_dreams")
// 1100 - windowing
@Keep
@@ -304,9 +282,7 @@ object Flags {
)
// TODO(b/293252410) : Tracking Bug
- @JvmField
- val LOCKSCREEN_ENABLE_LANDSCAPE =
- unreleasedFlag("lockscreen.enable_landscape")
+ @JvmField val LOCKSCREEN_ENABLE_LANDSCAPE = unreleasedFlag("lockscreen.enable_landscape")
// 1200 - predictive back
@Keep
@@ -327,8 +303,7 @@ object Flags {
val QUICK_TAP_IN_PCC = releasedFlag("quick_tap_in_pcc")
// TODO(b/261979569): Tracking Bug
- val QUICK_TAP_FLOW_FRAMEWORK =
- unreleasedFlag("quick_tap_flow_framework", teamfood = false)
+ val QUICK_TAP_FLOW_FRAMEWORK = unreleasedFlag("quick_tap_flow_framework", teamfood = false)
// 1500 - chooser aka sharesheet
@@ -364,14 +339,12 @@ object Flags {
// TODO(b/265764985): Tracking Bug
@Keep
@JvmField
- val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
- unreleasedFlag("enable_dark_vignette_when_folding")
+ val ENABLE_DARK_VIGNETTE_WHEN_FOLDING = unreleasedFlag("enable_dark_vignette_when_folding")
// TODO(b/265764985): Tracking Bug
@Keep
@JvmField
- val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS =
- unreleasedFlag("enable_unfold_status_bar_animations")
+ val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS = unreleasedFlag("enable_unfold_status_bar_animations")
// TODO(b/316157842): Tracking Bug
// Adds extra delay to notifications measure
@@ -415,28 +388,26 @@ object Flags {
unreleasedFlag("bigpicture_notification_lazy_loading")
// TODO(b/283740863): Tracking Bug
- @JvmField
- val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
+ @JvmField val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
// TODO(b/302144438): Tracking Bug
- @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
- unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
+ @JvmField
+ val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
+ unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
/** Enable the share wifi button in Quick Settings internet dialog. */
- @JvmField
- val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
+ @JvmField val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
/** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
- @JvmField
- val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
+ @JvmField val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
// TODO(b/300995746): Tracking Bug
/** A resource flag for whether the communal service is enabled. */
@JvmField
- val COMMUNAL_SERVICE_ENABLED = resourceBooleanFlag(R.bool.config_communalServiceEnabled,
- "communal_service_enabled")
+ val COMMUNAL_SERVICE_ENABLED =
+ resourceBooleanFlag(R.bool.config_communalServiceEnabled, "communal_service_enabled")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 871d04693452..0feb5ec277b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -35,8 +35,6 @@ import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionOldType;
import static android.view.WindowManager.TransitionType;
-import static com.android.systemui.Flags.refactorGetCurrentUser;
-
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -79,6 +77,7 @@ import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
@@ -319,6 +318,7 @@ public class KeyguardService extends Service {
private final WindowManagerOcclusionManager mWmOcclusionManager;
private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
private final KeyguardWakeDirectlyToGoneInteractor mKeyguardWakeDirectlyToGoneInteractor;
+ private final KeyguardDismissInteractor mKeyguardDismissInteractor;
private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
@Override
public FoldGracePeriodProvider get() {
@@ -346,7 +346,8 @@ public class KeyguardService extends Service {
KeyguardInteractor keyguardInteractor,
KeyguardEnabledInteractor keyguardEnabledInteractor,
Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
- KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor) {
+ KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
+ KeyguardDismissInteractor keyguardDismissInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -375,6 +376,7 @@ public class KeyguardService extends Service {
mWmOcclusionManager = windowManagerOcclusionManager;
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
+ mKeyguardDismissInteractor = keyguardDismissInteractor;
}
@Override
@@ -482,7 +484,11 @@ public class KeyguardService extends Service {
public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
trace("dismiss message=" + message);
checkPermission();
- mKeyguardViewMediator.dismiss(callback, message);
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardDismissInteractor.dismissKeyguardWithCallback(callback);
+ } else {
+ mKeyguardViewMediator.dismiss(callback, message);
+ }
}
@Override // Binder interface
@@ -672,9 +678,6 @@ public class KeyguardService extends Service {
public void setCurrentUser(int userId) {
trace("Deprecated/NOT USED: setCurrentUser userId=" + userId);
checkPermission();
- if (!refactorGetCurrentUser()) {
- mKeyguardViewMediator.setCurrentUser(userId);
- }
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 17c5977fc80a..8c82900810be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -41,7 +41,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
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.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.translucentOccludingActivityFix;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -626,11 +625,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onUserSwitching(int userId) {
- if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
+ Log.d(TAG, String.format("onUserSwitching %d", userId));
synchronized (KeyguardViewMediator.this) {
- if (refactorGetCurrentUser()) {
- notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
- }
+ notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
resetKeyguardDonePendingLocked();
dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
@@ -639,7 +636,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onUserSwitchComplete(int userId) {
- if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
+ Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
// We are calling dismiss again and with a delay as there are race conditions
// in some scenarios caused by async layout listeners
mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500);
@@ -1580,10 +1577,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- if (!refactorGetCurrentUser()) {
- KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
- }
-
// Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
// is disabled.
if (isKeyguardServiceEnabled()) {
@@ -2546,19 +2539,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
/**
- * Update the newUserId. Call while holding WindowManagerService lock.
- * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing.
- *
- * @param newUserId The id of the incoming user.
- */
- public void setCurrentUser(int newUserId) {
- KeyguardUpdateMonitor.setCurrentUser(newUserId);
- synchronized (this) {
- notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId));
- }
- }
-
- /**
* This broadcast receiver should be registered with the SystemUI permission.
*/
private final BroadcastReceiver mDelayedLockBroadcastReceiver = new BroadcastReceiver() {
@@ -3553,7 +3533,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId(true));
+ mSelectedUserInteractor.getSelectedUserId());
} catch (RemoteException e) {
Log.d(TAG, "Failed to force clear flags", e);
}
@@ -3591,7 +3571,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId(true));
+ mSelectedUserInteractor.getSelectedUserId());
} catch (RemoteException e) {
Log.d(TAG, "Failed to set disable flags: " + flags, e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
index 443e98762c47..208a17c0a220 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
@@ -9,3 +9,4 @@ chandruis@google.com
jglazier@google.com
mpietal@google.com
tsuji@google.com
+yuandizhou@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index 628e912253eb..d7e6bdb8f02c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -16,9 +16,13 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
@@ -28,23 +32,30 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
/** Encapsulates business logic for requesting the keyguard to dismiss/finish/done. */
@SysUISingleton
class KeyguardDismissInteractor
@Inject
constructor(
- trustRepository: TrustRepository,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ @Application private val scope: CoroutineScope,
private val keyguardRepository: KeyguardRepository,
- primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
+ trustRepository: TrustRepository,
alternateBouncerInteractor: AlternateBouncerInteractor,
powerInteractor: PowerInteractor,
- private val selectedUserInteractor: SelectedUserInteractor,
) {
/*
* Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -127,4 +138,29 @@ constructor(
suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
keyguardRepository.setKeyguardDone(keyguardDoneTiming)
}
+
+ /**
+ * Dismiss the keyguard (or show the bouncer) and invoke the provided callback once dismissed.
+ *
+ * TODO(b/358412565): Support dismiss messages.
+ */
+ fun dismissKeyguardWithCallback(
+ callback: IKeyguardDismissCallback?,
+ ) {
+ scope.launch {
+ withContext(mainDispatcher) {
+ if (callback != null) {
+ dismissCallbackRegistry.addCallback(callback)
+ }
+
+ // This will either show the bouncer, or dismiss the keyguard if insecure.
+ // We currently need to request showing the primary bouncer in order to start a
+ // transition to PRIMARY_BOUNCER. Once we refactor that so that starting the
+ // transition is what causes the bouncer to show, we can remove this entire method,
+ // and simply ask KeyguardTransitionInteractor to transition to a bouncer state or
+ // dismiss keyguard.
+ primaryBouncerInteractor.show(true)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 31b0bf7fe425..d9c48fa7e581 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -28,6 +28,7 @@ constructor(
private val interactors: Set<TransitionInteractor>,
private val auditLogger: KeyguardTransitionAuditLogger,
private val bootInteractor: KeyguardTransitionBootInteractor,
+ private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
) : CoreStartable {
override fun start() {
@@ -53,6 +54,7 @@ constructor(
}
auditLogger.start()
bootInteractor.start()
+ statusBarDisableFlagsInteractor.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
new file mode 100644
index 000000000000..47818cbfd2f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.annotation.SuppressLint
+import android.app.StatusBarManager
+import android.content.Context
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteException
+import android.provider.DeviceConfig
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.CoreStartable
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceconfig.domain.interactor.DeviceConfigInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.navigation.domain.interactor.NavigationInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Logic around StatusBarService#disableForUser, which is used to disable the home and recents
+ * button in certain device states.
+ *
+ * TODO(b/362313975): Remove post-Flexiglass, this duplicates StatusBarStartable logic.
+ */
+@SysUISingleton
+class StatusBarDisableFlagsInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ @Application private val applicationContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val statusBarService: IStatusBarService,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ selectedUserInteractor: SelectedUserInteractor,
+ deviceConfigInteractor: DeviceConfigInteractor,
+ navigationInteractor: NavigationInteractor,
+ authenticationInteractor: AuthenticationInteractor,
+ powerInteractor: PowerInteractor,
+) : CoreStartable {
+
+ private val disableToken: IBinder = Binder()
+
+ private val disableFlagsForUserId =
+ combine(
+ selectedUserInteractor.selectedUser,
+ keyguardTransitionInteractor.startedKeyguardState,
+ deviceConfigInteractor.property(
+ namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
+ name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ default = true,
+ ),
+ navigationInteractor.isGesturalMode,
+ authenticationInteractor.authenticationMethod,
+ powerInteractor.detailedWakefulness,
+ ) { values ->
+ val selectedUserId = values[0] as Int
+ val startedState = values[1] as KeyguardState
+ val isShowHomeOverLockscreen = values[2] as Boolean
+ val isGesturalMode = values[3] as Boolean
+ val authenticationMethod = values[4] as AuthenticationMethodModel
+ val wakefulnessModel = values[5] as WakefulnessModel
+ val isOccluded = startedState == KeyguardState.OCCLUDED
+
+ val hideHomeAndRecentsForBouncer =
+ startedState == KeyguardState.PRIMARY_BOUNCER ||
+ startedState == KeyguardState.ALTERNATE_BOUNCER
+ val isKeyguardShowing = startedState != KeyguardState.GONE
+ val isPowerGestureIntercepted =
+ with(wakefulnessModel) {
+ isAwake() &&
+ powerButtonLaunchGestureTriggered &&
+ lastSleepReason == WakeSleepReason.POWER_BUTTON
+ }
+
+ var flags = StatusBarManager.DISABLE_NONE
+
+ if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
+ if (!isShowHomeOverLockscreen || !isGesturalMode) {
+ flags = flags or StatusBarManager.DISABLE_HOME
+ }
+ flags = flags or StatusBarManager.DISABLE_RECENT
+ }
+
+ if (
+ isPowerGestureIntercepted &&
+ isOccluded &&
+ authenticationMethod.isSecure &&
+ deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+ ) {
+ flags = flags or StatusBarManager.DISABLE_RECENT
+ }
+
+ selectedUserId to flags
+ }
+ .distinctUntilChanged()
+
+ @SuppressLint("WrongConstant", "NonInjectedService")
+ override fun start() {
+ if (!KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
+ scope.launch {
+ disableFlagsForUserId.collect { (selectedUserId, flags) ->
+ if (applicationContext.getSystemService(Context.STATUS_BAR_SERVICE) == null) {
+ return@collect
+ }
+
+ withContext(backgroundDispatcher) {
+ try {
+ statusBarService.disableForUser(
+ flags,
+ disableToken,
+ applicationContext.packageName,
+ selectedUserId,
+ )
+ } catch (e: RemoteException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
index 9c29bab80d14..ed5080d66c33 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
@@ -22,7 +22,7 @@ import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManage
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -51,9 +51,8 @@ interface MediaDomainModule {
fun providesMediaDataManager(
legacyProvider: Provider<LegacyMediaDataManagerImpl>,
newProvider: Provider<MediaCarouselInteractor>,
- mediaFlags: MediaFlags,
): MediaDataManager {
- return if (mediaFlags.isSceneContainerEnabled()) {
+ return if (SceneContainerFlag.isEnabled) {
newProvider.get()
} else {
legacyProvider.get()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 9e2804edb9ff..916f8b2e1730 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -91,6 +91,7 @@ import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
@@ -272,7 +273,7 @@ class MediaDataProcessor(
}
override fun start() {
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 9d7160cbaffc..270ab72e291d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -105,7 +105,7 @@ constructor(
val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
override fun start() {
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index fb2bbde37a18..19cdee7befdd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -220,7 +220,7 @@ constructor(
private val animationScaleObserver: ContentObserver =
object : ContentObserver(executor, 0) {
override fun onChange(selfChange: Boolean) {
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
} else {
controllerById.values.forEach { it.updateAnimatorDurationScale() }
@@ -350,7 +350,7 @@ constructor(
inflateSettingsButton()
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
configurationController.addCallback(configListener)
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
setUpListeners()
} else {
val visualStabilityCallback = OnReorderingAllowedListener {
@@ -391,7 +391,7 @@ constructor(
listenForAnyStateToGoneKeyguardTransition(this)
listenForAnyStateToLockscreenTransition(this)
- if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
+ if (!SceneContainerFlag.isEnabled) return@repeatOnLifecycle
listenForMediaItemsChanges(this)
}
}
@@ -733,7 +733,7 @@ constructor(
when (commonViewModel) {
is MediaCommonViewModel.MediaControl -> {
val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
viewController.widthInSceneContainerPx = widthInSceneContainerPx
viewController.heightInSceneContainerPx = heightInSceneContainerPx
}
@@ -965,7 +965,7 @@ constructor(
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
if (existingPlayer == null) {
val newPlayer = mediaControlPanelFactory.get()
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
newPlayer.mediaViewController.widthInSceneContainerPx = widthInSceneContainerPx
newPlayer.mediaViewController.heightInSceneContainerPx =
heightInSceneContainerPx
@@ -1140,7 +1140,7 @@ constructor(
}
private fun updatePlayers(recreateMedia: Boolean) {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
updateMediaPlayers(recreateMedia)
return
}
@@ -1240,7 +1240,7 @@ constructor(
currentStartLocation = startLocation
currentEndLocation = endLocation
currentTransitionProgress = progress
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
for (mediaPlayer in MediaPlayerData.players()) {
updateViewControllerToState(mediaPlayer.mediaViewController, immediately)
}
@@ -1300,7 +1300,7 @@ constructor(
/** Update listening to seekbar. */
private fun updateSeekbarListening(visibleToUser: Boolean) {
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
for (player in MediaPlayerData.players()) {
player.setListening(visibleToUser && currentlyExpanded)
}
@@ -1313,7 +1313,7 @@ constructor(
private fun updateCarouselDimensions() {
var width = 0
var height = 0
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
for (mediaPlayer in MediaPlayerData.players()) {
val controller = mediaPlayer.mediaViewController
// When transitioning the view to gone, the view gets smaller, but the translation
@@ -1405,7 +1405,7 @@ constructor(
!mediaManager.hasActiveMediaOrRecommendation() &&
desiredHostState.showsOnlyActiveMedia
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
for (mediaPlayer in MediaPlayerData.players()) {
if (animate) {
mediaPlayer.mediaViewController.animatePendingStateChange(
@@ -1445,7 +1445,7 @@ constructor(
}
fun closeGuts(immediate: Boolean = true) {
- if (!mediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled) {
MediaPlayerData.players().forEach { it.closeGuts(immediate) }
} else {
controllerById.values.forEach { it.closeGuts(immediate) }
@@ -1596,7 +1596,7 @@ constructor(
@VisibleForTesting
fun onSwipeToDismiss() {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
mediaCarouselViewModel.onSwipeToDismiss(currentEndLocation)
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index addb0147889f..87610cf774a3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -111,7 +111,6 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder;
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
import com.android.systemui.media.controls.util.MediaDataUtils;
-import com.android.systemui.media.controls.util.MediaFlags;
import com.android.systemui.media.controls.util.MediaUiEventLogger;
import com.android.systemui.media.controls.util.SmallHash;
import com.android.systemui.media.dialog.MediaOutputDialogManager;
@@ -120,6 +119,7 @@ import com.android.systemui.monet.Style;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -209,7 +209,6 @@ public class MediaControlPanel {
static final long TURBULENCE_NOISE_PLAY_DURATION = 7500L;
private final SeekBarViewModel mSeekBarViewModel;
- private final MediaFlags mMediaFlags;
private final CommunalSceneInteractor mCommunalSceneInteractor;
private SeekBarObserver mSeekBarObserver;
protected final Executor mBackgroundExecutor;
@@ -323,8 +322,7 @@ public class MediaControlPanel {
CommunalSceneInteractor communalSceneInteractor,
NotificationLockscreenUserManager lockscreenUserManager,
BroadcastDialogController broadcastDialogController,
- GlobalSettings globalSettings,
- MediaFlags mediaFlags
+ GlobalSettings globalSettings
) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
@@ -343,7 +341,6 @@ public class MediaControlPanel {
mActivityIntentHelper = activityIntentHelper;
mLockscreenUserManager = lockscreenUserManager;
mBroadcastDialogController = broadcastDialogController;
- mMediaFlags = mediaFlags;
mCommunalSceneInteractor = communalSceneInteractor;
mSeekBarViewModel.setLogSeek(() -> {
@@ -641,7 +638,7 @@ public class MediaControlPanel {
// State refresh interferes with the translation animation, only run it if it's not running.
if (!mMetadataAnimationHandler.isRunning()) {
// Don't refresh in scene framework, because it will calculate with invalid layout sizes
- if (!mMediaFlags.isSceneContainerEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
mMediaViewController.refreshState();
}
}
@@ -909,7 +906,7 @@ public class MediaControlPanel {
// Capture width & height from views in foreground for artwork scaling in background
int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
- if (mMediaFlags.isSceneContainerEnabled() && (width <= 0 || height <= 0)) {
+ if (SceneContainerFlag.isEnabled() && (width <= 0 || height <= 0)) {
// TODO(b/312714128): ensure we have a valid size before setting background
width = mMediaViewController.getWidthInSceneContainerPx();
height = mMediaViewController.getHeightInSceneContainerPx();
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 091b886c7ba4..a9d2a541a241 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -46,10 +46,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.view.MediaHost
-import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.StatusBarState
@@ -119,7 +119,6 @@ constructor(
@Application private val coroutineScope: CoroutineScope,
private val splitShadeStateController: SplitShadeStateController,
private val logger: MediaViewLogger,
- private val mediaFlags: MediaFlags,
) {
/** Track the media player setting status on lock screen. */
@@ -1111,7 +1110,7 @@ constructor(
private fun updateHostAttachment() =
traceSection("MediaHierarchyManager#updateHostAttachment") {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
// No need to manage transition states - just update the desired location directly
logger.logMediaHostAttachment(desiredLocation)
mediaCarouselController.onDesiredLocationChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 584908ff2aad..e57de09f1063 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -46,8 +46,8 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.surfaceeffects.PaintDrawCallback
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -82,7 +82,6 @@ constructor(
private val logger: MediaViewLogger,
private val seekBarViewModel: SeekBarViewModel,
@Main private val mainExecutor: DelayableExecutor,
- private val mediaFlags: MediaFlags,
private val globalSettings: GlobalSettings,
) {
@@ -125,7 +124,7 @@ constructor(
set(value) {
if (field != value) {
field = value
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
locationChangeListener(value)
}
}
@@ -212,7 +211,7 @@ constructor(
private val scrubbingChangeListener =
object : SeekBarViewModel.ScrubbingChangeListener {
override fun onScrubbingChanged(scrubbing: Boolean) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
if (isScrubbing == scrubbing) return
isScrubbing = scrubbing
updateDisplayForScrubbingChange()
@@ -222,7 +221,7 @@ constructor(
private val enabledChangeListener =
object : SeekBarViewModel.EnabledChangeListener {
override fun onEnabledChanged(enabled: Boolean) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
if (isSeekBarEnabled == enabled) return
isSeekBarEnabled = enabled
MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
@@ -238,7 +237,7 @@ constructor(
* @param listening True when player should be active. Otherwise, false.
*/
fun setListening(listening: Boolean) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
seekBarViewModel.listening = listening
}
@@ -272,7 +271,7 @@ constructor(
)
)
}
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
if (
this@MediaViewController::recsConfigurationChangeListener.isInitialized
) {
@@ -344,7 +343,7 @@ constructor(
* Notify this controller that the view has been removed and all listeners should be destroyed
*/
fun onDestroy() {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
if (this::seekBarObserver.isInitialized) {
seekBarViewModel.progress.removeObserver(seekBarObserver)
}
@@ -565,7 +564,7 @@ constructor(
state: MediaHostState?,
isGutsAnimation: Boolean = false
): TransitionViewState? {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
return obtainSceneContainerViewState()
}
@@ -667,7 +666,7 @@ constructor(
}
fun attachPlayer(mediaViewHolder: MediaViewHolder) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
this.mediaViewHolder = mediaViewHolder
// Setting up seek bar.
@@ -741,7 +740,7 @@ constructor(
}
fun updateAnimatorDurationScale() {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
if (this::seekBarObserver.isInitialized) {
seekBarObserver.animationEnabled =
globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f
@@ -801,7 +800,7 @@ constructor(
}
fun attachRecommendations(recommendationViewHolder: RecommendationViewHolder) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
this.recommendationViewHolder = recommendationViewHolder
attach(recommendationViewHolder.recommendations, TYPE.RECOMMENDATION)
@@ -810,13 +809,13 @@ constructor(
}
fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
seekBarViewModel.logSeek = onSeek
onBindSeekBar(seekBarViewModel)
}
fun setUpTurbulenceNoise() {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
mediaViewHolder!!.let {
if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
turbulenceNoiseAnimationConfig =
@@ -1049,7 +1048,7 @@ constructor(
*/
private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? {
val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
return obtainSceneContainerViewState()
}
@@ -1080,7 +1079,7 @@ constructor(
/** Clear all existing measurements and refresh the state to match the view. */
fun refreshState() =
traceSection("MediaViewController#refreshState") {
- if (mediaFlags.isSceneContainerEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
// We don't need to recreate measurements for scene container, since it's a known
// size. Just get the view state and update the layout controller
obtainSceneContainerViewState()?.let {
@@ -1169,13 +1168,13 @@ constructor(
}
fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
isPrevButtonAvailable = isAvailable
prevNotVisibleValue = notVisibleValue
}
fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
- if (!mediaFlags.isSceneContainerEnabled()) return
+ if (!SceneContainerFlag.isEnabled) return
isNextButtonAvailable = isAvailable
nextNotVisibleValue = notVisibleValue
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 21c311191710..a65243dfe315 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -21,7 +21,6 @@ import android.os.UserHandle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import javax.inject.Inject
@SysUISingleton
@@ -49,7 +48,4 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlagsClass
/** Check whether we allow remote media to generate resume controls */
fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
-
- /** Check whether to use scene framework */
- fun isSceneContainerEnabled() = SceneContainerFlag.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 42f66cca2522..7d2a1e178dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -18,7 +18,6 @@ package com.android.systemui.model
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.Flags.glanceableHubBackGesture
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -107,10 +106,7 @@ constructor(
{
it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion
},
- SYSUI_STATE_COMMUNAL_HUB_SHOWING to
- {
- glanceableHubBackGesture() && it.scene == Scenes.Communal
- }
+ SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.scene == Scenes.Communal }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index ac878c2d698d..6f82d5dfff15 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -19,19 +19,24 @@ package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON;
import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import android.content.ContentResolver;
import android.content.Context;
@@ -60,10 +65,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
@@ -107,6 +113,7 @@ public final class NavBarHelper implements
AccessibilityManager.AccessibilityServicesStateChangeListener,
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener,
+ AccessibilityGestureTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
Dumpable, CommandQueue.Callbacks, ConfigurationController.ConfigurationListener {
private static final String TAG = NavBarHelper.class.getSimpleName();
@@ -122,6 +129,7 @@ public final class NavBarHelper implements
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
+ private final AccessibilityGestureTargetsObserver mAccessibilityGestureTargetsObserver;
private final List<NavbarTaskbarStateUpdater> mStateListeners = new ArrayList<>();
private final Context mContext;
private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -188,6 +196,7 @@ public final class NavBarHelper implements
public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
+ AccessibilityGestureTargetsObserver accessibilityGestureTargetsObserver,
SystemActions systemActions,
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
@@ -220,6 +229,7 @@ public final class NavBarHelper implements
mSystemActions = systemActions;
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
+ mAccessibilityGestureTargetsObserver = accessibilityGestureTargetsObserver;
mWm = wm;
mDefaultDisplayId = displayTracker.getDefaultDisplayId();
mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context);
@@ -249,6 +259,7 @@ public final class NavBarHelper implements
mAccessibilityManager.addAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver.addListener(this);
mAccessibilityButtonTargetsObserver.addListener(this);
+ mAccessibilityGestureTargetsObserver.addListener(this);
// Setup assistant listener
mContentResolver.registerContentObserver(
@@ -291,6 +302,7 @@ public final class NavBarHelper implements
mAccessibilityManager.removeAccessibilityServicesStateChangeListener(this);
mAccessibilityButtonModeObserver.removeListener(this);
mAccessibilityButtonTargetsObserver.removeListener(this);
+ mAccessibilityGestureTargetsObserver.removeListener(this);
// Clean up assistant listeners
mContentResolver.unregisterContentObserver(mAssistContentObserver);
@@ -380,43 +392,50 @@ public final class NavBarHelper implements
}
@Override
+ public void onAccessibilityGestureTargetsChanged(String targets) {
+ updateA11yState();
+ }
+
+ @Override
public void onConfigChanged(Configuration newConfig) {
mEdgeBackGestureHandler.onConfigurationChanged(newConfig);
}
+ private int getNumOfA11yShortcutTargetsForNavSystem() {
+ final int buttonMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+ final int shortcutType;
+ if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
+ shortcutType = buttonMode
+ != ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU ? SOFTWARE : DEFAULT;
+ // If accessibility button is floating menu mode, there are no clickable targets.
+ } else {
+ if (mNavBarMode == NAV_BAR_MODE_GESTURAL) {
+ shortcutType = GESTURE;
+ } else {
+ shortcutType = buttonMode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR
+ ? SOFTWARE : DEFAULT;
+ }
+ }
+ return mAccessibilityManager.getAccessibilityShortcutTargets(shortcutType).size();
+ }
+
/**
* Updates the current accessibility button state. The accessibility button state is only
* used for {@link Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR} and
* {@link Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}, otherwise it is reset to 0.
*/
- private void updateA11yState() {
+ @VisibleForTesting
+ void updateA11yState() {
final long prevState = mA11yButtonState;
final boolean clickable;
final boolean longClickable;
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- clickable = false;
- longClickable = false;
- mA11yButtonState = 0;
- } else {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS
- // permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- ShortcutConstants.UserShortcutType.SOFTWARE);
- final int requestingServices = a11yButtonTargets.size();
-
- clickable = requestingServices >= 1;
-
- // `longClickable` is used to determine whether to pop up the accessibility chooser
- // dialog or not, and it’s also only for multiple services.
- longClickable = requestingServices >= 2;
- mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
+ int clickableServices = getNumOfA11yShortcutTargetsForNavSystem();
+ clickable = clickableServices >= 1;
+ // `longClickable` is used to determine whether to pop up the accessibility chooser
+ // dialog or not, and it’s also only for multiple services.
+ longClickable = clickableServices >= 2;
+ mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
// Update the system actions if the state has changed
if (prevState != mA11yButtonState) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8887f5857baf..9abc494e56e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -71,6 +71,7 @@ import com.android.systemui.qs.SideLabelTileLayout;
import com.android.systemui.qs.logging.QSLogger;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Base quick-settings tile, extend this to create a new tile.
@@ -350,6 +351,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
public void userSwitch(int newUserId) {
mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
+ postStale();
}
public void destroy() {
@@ -667,6 +669,18 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
public String toString() {
return "DrawableIcon";
}
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ // No need to compare equality of the mInvisibleDrawable as that's generated from
+ // mDrawable's constant state.
+ return other instanceof DrawableIcon && ((DrawableIcon) other).mDrawable == mDrawable;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDrawable);
+ }
}
public static class DrawableIconWithRes extends DrawableIcon {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ecf816b263ff..fe5cbb18f046 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -26,7 +26,6 @@ import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
-import static com.android.systemui.Flags.glanceableHubBackGesture;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
@@ -86,10 +85,10 @@ import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenshotRequest;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -231,7 +230,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
// If scene framework is enabled, set the scene container window to
// visible and let the touch "slip" into that window.
if (SceneContainerFlag.isEnabled()) {
- mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
+ mSceneInteractor.get().onRemoteUserInputStarted("launcher swipe");
} else {
mShadeViewControllerLazy.get().startInputFocusTransfer();
}
@@ -267,7 +266,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
if (SceneContainerFlag.isEnabled()) {
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
- mSceneInteractor.get().onRemoteUserInteractionStarted(
+ mSceneInteractor.get().onRemoteUserInputStarted(
"trackpad swipe");
} else if (action == ACTION_UP) {
mSceneInteractor.get().changeScene(
@@ -837,8 +836,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
.setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
.setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
.setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming)
- .setFlag(SYSUI_STATE_COMMUNAL_HUB_SHOWING,
- glanceableHubBackGesture() && communalShowing)
+ .setFlag(SYSUI_STATE_COMMUNAL_HUB_SHOWING, communalShowing)
.commitUpdate(mContext.getDisplayId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 3e2c6306467f..beb6816d70a9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -56,7 +56,10 @@ constructor(
*
* For more information see the logic in `SceneInteractor` that mutates this.
*/
- val isRemoteUserInteractionOngoing = MutableStateFlow(false)
+ val isRemoteUserInputOngoing = MutableStateFlow(false)
+
+ /** Whether there's ongoing user input on the scene container Composable hierarchy */
+ val isSceneContainerUserInputOngoing = MutableStateFlow(false)
private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 1b9c346129c6..4c404e29018d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -148,11 +148,11 @@ constructor(
val isVisible: StateFlow<Boolean> =
combine(
repository.isVisible,
- repository.isRemoteUserInteractionOngoing,
+ repository.isRemoteUserInputOngoing,
) { isVisible, isRemoteUserInteractionOngoing ->
isVisibleInternal(
raw = isVisible,
- isRemoteUserInteractionOngoing = isRemoteUserInteractionOngoing,
+ isRemoteUserInputOngoing = isRemoteUserInteractionOngoing,
)
}
.stateIn(
@@ -162,8 +162,13 @@ constructor(
)
/** Whether there's an ongoing remotely-initiated user interaction. */
- val isRemoteUserInteractionOngoing: StateFlow<Boolean> =
- repository.isRemoteUserInteractionOngoing
+ val isRemoteUserInteractionOngoing: StateFlow<Boolean> = repository.isRemoteUserInputOngoing
+
+ /**
+ * Whether there's an ongoing user interaction started in the scene container Compose hierarchy.
+ */
+ val isSceneContainerUserInputOngoing: StateFlow<Boolean> =
+ repository.isSceneContainerUserInputOngoing
/**
* The amount of transition into or out of the given [scene].
@@ -284,7 +289,7 @@ constructor(
* Please do not call this from outside of the scene framework. If you are trying to force the
* visibility to visible or invisible, prefer making changes to the existing caller of this
* method or to upstream state used to calculate [isVisible]; for an example of the latter,
- * please see [onRemoteUserInteractionStarted] and [onUserInteractionFinished].
+ * please see [onRemoteUserInputStarted] and [onUserInputFinished].
*/
fun setVisible(isVisible: Boolean, loggingReason: String) {
val wasVisible = repository.isVisible.value
@@ -301,6 +306,16 @@ constructor(
}
/**
+ * Notifies that a scene container user interaction has begun.
+ *
+ * This is a user interaction that originates within the Composable hierarchy of the scene
+ * container.
+ */
+ fun onSceneContainerUserInputStarted() {
+ repository.isSceneContainerUserInputOngoing.value = true
+ }
+
+ /**
* Notifies that a remote user interaction has begun.
*
* This is a user interaction that originates outside of the UI of the scene container and
@@ -311,18 +326,19 @@ constructor(
* then rerouted by window manager to System UI. While the user interaction definitely continues
* within the System UI process and code, it also originates remotely.
*/
- fun onRemoteUserInteractionStarted(loggingReason: String) {
- logger.logRemoteUserInteractionStarted(loggingReason)
- repository.isRemoteUserInteractionOngoing.value = true
+ fun onRemoteUserInputStarted(loggingReason: String) {
+ logger.logRemoteUserInputStarted(loggingReason)
+ repository.isRemoteUserInputOngoing.value = true
}
/**
* Notifies that the current user interaction (internally or remotely started, see
- * [onRemoteUserInteractionStarted]) has finished.
+ * [onSceneContainerUserInputStarted] and [onRemoteUserInputStarted]) has finished.
*/
- fun onUserInteractionFinished() {
- logger.logUserInteractionFinished()
- repository.isRemoteUserInteractionOngoing.value = false
+ fun onUserInputFinished() {
+ logger.logUserInputFinished()
+ repository.isSceneContainerUserInputOngoing.value = false
+ repository.isRemoteUserInputOngoing.value = false
}
/**
@@ -351,9 +367,9 @@ constructor(
private fun isVisibleInternal(
raw: Boolean = repository.isVisible.value,
- isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
+ isRemoteUserInputOngoing: Boolean = repository.isRemoteUserInputOngoing.value,
): Boolean {
- return raw || isRemoteUserInteractionOngoing
+ return raw || isRemoteUserInputOngoing
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
index 893f030fab4d..d7413687eeae 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
@@ -166,7 +166,7 @@ constructor(
StatusBarManager.DISABLE_NONE,
disableToken,
applicationContext.packageName,
- selectedUserInteractor.getSelectedUserId(true),
+ selectedUserInteractor.getSelectedUserId(),
)
} catch (e: RemoteException) {
Log.d(TAG, "Failed to clear flags", e)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index 94c94e22a10b..045a8879f572 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -115,7 +115,7 @@ class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer:
)
}
- fun logRemoteUserInteractionStarted(
+ fun logRemoteUserInputStarted(
reason: String,
) {
logBuffer.log(
@@ -126,7 +126,7 @@ class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer:
)
}
- fun logUserInteractionFinished() {
+ fun logUserInputFinished() {
logBuffer.log(
tag = TAG,
level = LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 9dfb7450fd3f..8b4b77f83218 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -97,7 +98,9 @@ constructor(
}
/**
- * Notifies that a [MotionEvent] is first seen at the top of the scene container UI.
+ * Notifies that a [MotionEvent] is first seen at the top of the scene container UI. This
+ * includes gestures on [SharedNotificationContainer] as well as the Composable scene container
+ * hierarchy.
*
* Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
*/
@@ -108,11 +111,21 @@ constructor(
event.actionMasked == MotionEvent.ACTION_UP ||
event.actionMasked == MotionEvent.ACTION_CANCEL
) {
- sceneInteractor.onUserInteractionFinished()
+ sceneInteractor.onUserInputFinished()
}
}
/**
+ * Notifies that a scene container user interaction has begun.
+ *
+ * This is a user interaction that has reached the Composable hierarchy of the scene container,
+ * rather than being handled by [SharedNotificationContainer].
+ */
+ fun onSceneContainerUserInputStarted() {
+ sceneInteractor.onSceneContainerUserInputStarted()
+ }
+
+ /**
* Notifies that a [MotionEvent] that was previously sent to [onMotionEvent] has passed through
* the scene container UI.
*
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 0a1f649691a1..ed590c37c384 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -36,6 +36,13 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.util.Assert
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import javax.inject.Provider
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -44,30 +51,23 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
-import java.io.PrintWriter
-import java.lang.ref.WeakReference
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.Executor
-import javax.inject.Provider
-import kotlin.properties.ReadWriteProperty
-import kotlin.reflect.KProperty
/**
* SystemUI cache for keeping track of the current user and associated values.
*
- * The values provided asynchronously are NOT copies, but shared among all requesters. Do not
- * modify them.
+ * The values provided asynchronously are NOT copies, but shared among all requesters. Do not modify
+ * them.
*
* This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as
- * soon as possible (and reduce its dependency graph).
- * Other classes that want to listen to the broadcasts listened here SHOULD
- * subscribe to this class instead.
+ * soon as possible (and reduce its dependency graph). Other classes that want to listen to the
+ * broadcasts listened here SHOULD subscribe to this class instead.
*
* @see UserTracker
*
* Class constructed and initialized in [SettingsModule].
*/
-open class UserTrackerImpl internal constructor(
+open class UserTrackerImpl
+internal constructor(
private val context: Context,
private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
private val userManager: UserManager,
@@ -87,8 +87,8 @@ open class UserTrackerImpl internal constructor(
private set
private val mutex = Any()
- private val isBackgroundUserSwitchEnabled: Boolean get() =
- featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
+ private val isBackgroundUserSwitchEnabled: Boolean
+ get() = featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
@Deprecated("Use UserInteractor.getSelectedUserId()")
override var userId: Int by SynchronizedDelegate(context.userId)
@@ -118,8 +118,7 @@ open class UserTrackerImpl internal constructor(
override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList())
protected set
- @GuardedBy("callbacks")
- private val callbacks: MutableList<DataItem> = ArrayList()
+ @GuardedBy("callbacks") private val callbacks: MutableList<DataItem> = ArrayList()
private var userSwitchingJob: Job? = null
private var afterUserSwitchingJob: Job? = null
@@ -128,23 +127,25 @@ open class UserTrackerImpl internal constructor(
if (initialized) {
return
}
+ Log.i(TAG, "Starting user: $startingUser")
initialized = true
setUserIdInternal(startingUser)
- val filter = IntentFilter().apply {
- addAction(Intent.ACTION_LOCALE_CHANGED)
- addAction(Intent.ACTION_USER_INFO_CHANGED)
- addAction(Intent.ACTION_PROFILE_ADDED)
- addAction(Intent.ACTION_PROFILE_REMOVED)
- addAction(Intent.ACTION_PROFILE_AVAILABLE)
- addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
- // These get called when a managed profile goes in or out of quiet mode.
- addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
- addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
- addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
- addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
- addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
- }
+ val filter =
+ IntentFilter().apply {
+ addAction(Intent.ACTION_LOCALE_CHANGED)
+ addAction(Intent.ACTION_USER_INFO_CHANGED)
+ addAction(Intent.ACTION_PROFILE_ADDED)
+ addAction(Intent.ACTION_PROFILE_REMOVED)
+ addAction(Intent.ACTION_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
+ // These get called when a managed profile goes in or out of quiet mode.
+ addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+ addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+ addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
+ }
context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
registerUserSwitchObserver()
@@ -191,36 +192,39 @@ open class UserTrackerImpl internal constructor(
}
private fun registerUserSwitchObserver() {
- iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
- override fun onBeforeUserSwitching(newUserId: Int) {
- handleBeforeUserSwitching(newUserId)
- }
+ iActivityManager.registerUserSwitchObserver(
+ object : UserSwitchObserver() {
+ override fun onBeforeUserSwitching(newUserId: Int) {
+ handleBeforeUserSwitching(newUserId)
+ }
- override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
- if (isBackgroundUserSwitchEnabled) {
- userSwitchingJob?.cancel()
- userSwitchingJob = appScope.launch(backgroundContext) {
- handleUserSwitchingCoroutines(newUserId) {
- reply?.sendResult(null)
- }
+ override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+ if (isBackgroundUserSwitchEnabled) {
+ userSwitchingJob?.cancel()
+ userSwitchingJob =
+ appScope.launch(backgroundContext) {
+ handleUserSwitchingCoroutines(newUserId) { reply?.sendResult(null) }
+ }
+ } else {
+ handleUserSwitching(newUserId)
+ reply?.sendResult(null)
}
- } else {
- handleUserSwitching(newUserId)
- reply?.sendResult(null)
}
- }
- override fun onUserSwitchComplete(newUserId: Int) {
- if (isBackgroundUserSwitchEnabled) {
- afterUserSwitchingJob?.cancel()
- afterUserSwitchingJob = appScope.launch(backgroundContext) {
+ override fun onUserSwitchComplete(newUserId: Int) {
+ if (isBackgroundUserSwitchEnabled) {
+ afterUserSwitchingJob?.cancel()
+ afterUserSwitchingJob =
+ appScope.launch(backgroundContext) {
+ handleUserSwitchComplete(newUserId)
+ }
+ } else {
handleUserSwitchComplete(newUserId)
}
- } else {
- handleUserSwitchComplete(newUserId)
}
- }
- }, TAG)
+ },
+ TAG
+ )
}
@WorkerThread
@@ -228,9 +232,10 @@ open class UserTrackerImpl internal constructor(
setUserIdInternal(newUserId)
notifySubscribers { callback, resultCallback ->
- callback.onBeforeUserSwitching(newUserId)
- resultCallback.run()
- }.await()
+ callback.onBeforeUserSwitching(newUserId)
+ resultCallback.run()
+ }
+ .await()
}
@WorkerThread
@@ -239,31 +244,34 @@ open class UserTrackerImpl internal constructor(
Log.i(TAG, "Switching to user $newUserId")
notifySubscribers { callback, resultCallback ->
- callback.onUserChanging(newUserId, userContext, resultCallback)
- }.await()
+ callback.onUserChanging(newUserId, userContext, resultCallback)
+ }
+ .await()
}
@WorkerThread
protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
- coroutineScope {
- Assert.isNotMainThread()
- Log.i(TAG, "Switching to user $newUserId")
+ coroutineScope {
+ Assert.isNotMainThread()
+ Log.i(TAG, "Switching to user $newUserId")
- for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
- val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
- launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+ for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+ val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+ launch(callbackDataItem.executor.asCoroutineDispatcher()) {
val mutex = Mutex(true)
- val thresholdLogJob = launch(backgroundContext) {
- delay(USER_CHANGE_THRESHOLD)
- Log.e(TAG, "Failed to finish $callback in time")
- }
+ val thresholdLogJob =
+ launch(backgroundContext) {
+ delay(USER_CHANGE_THRESHOLD)
+ Log.e(TAG, "Failed to finish $callback in time")
+ }
callback.onUserChanging(userId, userContext) { mutex.unlock() }
mutex.lock()
thresholdLogJob.cancel()
- }.join()
- }
- onDone()
+ }
+ .join()
}
+ onDone()
+ }
@WorkerThread
protected open fun handleUserSwitchComplete(newUserId: Int) {
@@ -284,36 +292,26 @@ open class UserTrackerImpl internal constructor(
synchronized(mutex) {
userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
}
- notifySubscribers { callback, _ ->
- callback.onProfilesChanged(profiles)
- }
+ notifySubscribers { callback, _ -> callback.onProfilesChanged(profiles) }
}
override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
- synchronized(callbacks) {
- callbacks.add(DataItem(WeakReference(callback), executor))
- }
+ synchronized(callbacks) { callbacks.add(DataItem(WeakReference(callback), executor)) }
}
override fun removeCallback(callback: UserTracker.Callback) {
- synchronized(callbacks) {
- callbacks.removeIf { it.sameOrEmpty(callback) }
- }
+ synchronized(callbacks) { callbacks.removeIf { it.sameOrEmpty(callback) } }
}
private inline fun notifySubscribers(
- crossinline action: (UserTracker.Callback, resultCallback: Runnable) -> Unit
+ crossinline action: (UserTracker.Callback, resultCallback: Runnable) -> Unit
): CountDownLatch {
- val list = synchronized(callbacks) {
- callbacks.toList()
- }
+ val list = synchronized(callbacks) { callbacks.toList() }
val latch = CountDownLatch(list.size)
list.forEach {
val callback = it.callback.get()
if (callback != null) {
- it.executor.execute {
- action(callback) { latch.countDown() }
- }
+ it.executor.execute { action(callback) { latch.countDown() } }
} else {
latch.countDown()
}
@@ -328,20 +326,13 @@ open class UserTrackerImpl internal constructor(
val ids = userProfiles.map { it.toFullString() }
pw.println("userProfiles: $ids")
}
- val list = synchronized(callbacks) {
- callbacks.toList()
- }
+ val list = synchronized(callbacks) { callbacks.toList() }
pw.println("Callbacks:")
- list.forEach {
- it.callback.get()?.let {
- pw.println(" $it")
- }
- }
+ list.forEach { it.callback.get()?.let { pw.println(" $it") } }
}
- private class SynchronizedDelegate<T : Any>(
- private var value: T
- ) : ReadWriteProperty<UserTrackerImpl, T> {
+ private class SynchronizedDelegate<T : Any>(private var value: T) :
+ ReadWriteProperty<UserTrackerImpl, T> {
@GuardedBy("mutex")
override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 6223ca778815..4639e2235346 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.content.Context
-import android.graphics.Insets
import android.graphics.Rect
import android.os.PowerManager
import android.os.SystemClock
@@ -26,7 +25,6 @@ import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
-import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
@@ -42,7 +40,6 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Flags
-import com.android.systemui.Flags.glanceableHubBackGesture
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
@@ -322,21 +319,13 @@ constructor(
// Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not
// occluded.
lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
- // Avoid adding exclusion to end/start edges to allow back gestures.
- val insets =
- if (glanceableHubBackGesture()) {
- containerView.rootWindowInsets.getInsets(WindowInsets.Type.systemGestures())
- } else {
- Insets.NONE
- }
-
val ltr = containerView.layoutDirection == View.LAYOUT_DIRECTION_LTR
val backGestureInset =
Rect(
- if (ltr) 0 else insets.left,
0,
- if (ltr) insets.right else containerView.right,
+ 0,
+ if (ltr) 0 else containerView.right,
containerView.bottom,
)
@@ -352,9 +341,9 @@ constructor(
// Only allow swipe up to bouncer and swipe down to shade in the very
// top/bottom to avoid conflicting with widgets in the hub grid.
Rect(
- insets.left,
+ 0,
topEdgeSwipeRegionWidth,
- containerView.right - insets.right,
+ containerView.right,
containerView.bottom - bottomEdgeSwipeRegionWidth
),
// Disable back gestures on the left side of the screen, to avoid
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
index 2f9848863059..f270e821840a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -17,14 +17,15 @@
package com.android.systemui.shade.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
-import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.BooleanFlowOperators.any
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
/** Models UI state for the shade window. */
@SysUISingleton
@@ -32,11 +33,38 @@ class NotificationShadeWindowModel
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- keyguardInteractor: KeyguardInteractor,
) {
+ /**
+ * Considered to be occluded if in OCCLUDED, DREAMING, GLANCEABLE_HUB/Communal, or transitioning
+ * between those states. Every permutation is listed so we can use optimal flows and support
+ * Scenes.
+ */
val isKeyguardOccluded: Flow<Boolean> =
- anyOf(
- keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
- keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
- )
+ listOf(
+ // Finished in state...
+ keyguardTransitionInteractor.isFinishedIn(OCCLUDED),
+ keyguardTransitionInteractor.isFinishedIn(DREAMING),
+ keyguardTransitionInteractor.isFinishedIn(Scenes.Communal, GLANCEABLE_HUB),
+
+ // ... or transitions between those states
+ keyguardTransitionInteractor.isInTransition(Edge.create(OCCLUDED, DREAMING)),
+ keyguardTransitionInteractor.isInTransition(Edge.create(DREAMING, OCCLUDED)),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = OCCLUDED, to = Scenes.Communal),
+ edgeWithoutSceneContainer = Edge.create(from = OCCLUDED, to = GLANCEABLE_HUB),
+ ),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
+ edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED),
+ ),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = DREAMING, to = Scenes.Communal),
+ edgeWithoutSceneContainer = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
+ ),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+ edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+ ),
+ )
+ .any()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d0c51bc28126..bf00a39c42ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1255,6 +1255,11 @@ public class NotificationStackScrollLayout
}
@Override
+ public void closeGutsOnSceneTouch() {
+ mController.closeControlsDueToOutsideTouch();
+ }
+
+ @Override
public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
mScrollViewFields.setSyntheticScrollConsumer(consumer);
}
@@ -1265,6 +1270,11 @@ public class NotificationStackScrollLayout
}
@Override
+ public void setCurrentGestureInGutsConsumer(@Nullable Consumer<Boolean> consumer) {
+ mScrollViewFields.setCurrentGestureInGutsConsumer(consumer);
+ }
+
+ @Override
public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
mScrollViewFields.setHeadsUpHeightConsumer(consumer);
}
@@ -3548,33 +3558,41 @@ public class NotificationStackScrollLayout
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (SceneContainerFlag.isEnabled() && mIsBeingDragged) {
+ if (SceneContainerFlag.isEnabled()) {
int action = ev.getActionMasked();
- boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
- if (mSendingTouchesToSceneFramework) {
- MotionEvent adjustedEvent = MotionEvent.obtain(ev);
- adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
- mController.sendTouchToSceneFramework(adjustedEvent);
- mScrollViewFields.sendCurrentGestureOverscroll(
- getExpandedInThisMotion() && !isUpOrCancel);
- adjustedEvent.recycle();
- } else if (!isUpOrCancel) {
- // if this is the first touch being sent to the scene framework,
- // convert it into a synthetic DOWN event.
- mSendingTouchesToSceneFramework = true;
- MotionEvent downEvent = MotionEvent.obtain(ev);
- downEvent.setAction(MotionEvent.ACTION_DOWN);
- downEvent.setLocation(ev.getRawX(), ev.getRawY());
- mController.sendTouchToSceneFramework(downEvent);
- mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
- downEvent.recycle();
- }
-
- if (isUpOrCancel) {
- mScrollViewFields.sendCurrentGestureOverscroll(false);
- setIsBeingDragged(false);
+ boolean isTouchInGuts = mController.isTouchInGutsView(ev);
+ if (action == MotionEvent.ACTION_DOWN && !isTouchInGuts) {
+ mController.closeControlsDueToOutsideTouch();
+ }
+ if (mIsBeingDragged) {
+ boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
+ if (mSendingTouchesToSceneFramework) {
+ MotionEvent adjustedEvent = MotionEvent.obtain(ev);
+ adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
+ mScrollViewFields.sendCurrentGestureOverscroll(
+ getExpandedInThisMotion() && !isUpOrCancel);
+ mController.sendTouchToSceneFramework(adjustedEvent);
+ adjustedEvent.recycle();
+ } else if (!isUpOrCancel) {
+ // if this is the first touch being sent to the scene framework,
+ // convert it into a synthetic DOWN event.
+ mSendingTouchesToSceneFramework = true;
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ downEvent.setLocation(ev.getRawX(), ev.getRawY());
+ mScrollViewFields.sendCurrentGestureInGuts(isTouchInGuts);
+ mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
+ mController.sendTouchToSceneFramework(downEvent);
+ downEvent.recycle();
+ }
+
+ if (isUpOrCancel) {
+ mScrollViewFields.sendCurrentGestureInGuts(false);
+ mScrollViewFields.sendCurrentGestureOverscroll(false);
+ setIsBeingDragged(false);
+ }
+ return false;
}
- return false;
}
return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c25b30db7754..4e73529b911d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1690,7 +1690,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mVisibilityProvider.obtain(entry, true));
}
- public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ private View getGutsView() {
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
View translatingParentView = mSwipeHelper.getTranslatingParentView();
@@ -1703,15 +1703,35 @@ public class NotificationStackScrollLayoutController implements Dumpable {
// Checking menu
view = translatingParentView;
}
+ return view;
+ }
+
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ SceneContainerFlag.assertInLegacyMode();
+ View view = getGutsView();
if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
// Touch was outside visible guts / menu notification, close what's visible
- mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
- false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+ closeAndSaveGuts();
}
}
+ void closeControlsDueToOutsideTouch() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ closeAndSaveGuts();
+ }
+
+ private void closeAndSaveGuts() {
+ mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+ }
+
+ boolean isTouchInGutsView(MotionEvent event) {
+ View view = getGutsView();
+ return NotificationSwipeHelper.isTouchInView(event, view);
+ }
+
public void clearSilentNotifications() {
FooterViewRefactor.assertInLegacyMode();
// Leave the shade open if there will be other notifs left over to clear
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index 383d8b3b26b5..aa3953987c10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -53,6 +53,11 @@ class ScrollViewFields {
*/
var currentGestureOverscrollConsumer: Consumer<Boolean>? = null
/**
+ * When a gesture is on open notification guts, which means scene container should not close the
+ * guts off of this gesture, we can notify the placeholder through here.
+ */
+ var currentGestureInGutsConsumer: Consumer<Boolean>? = null
+ /**
* Any time the heads up height is recalculated, it should be updated here to be used by the
* placeholder
*/
@@ -66,6 +71,10 @@ class ScrollViewFields {
fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) =
currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll)
+ /** send [isCurrentGestureInGuts] to the [currentGestureInGutsConsumer], if present. */
+ fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) =
+ currentGestureInGutsConsumer?.accept(isCurrentGestureInGuts)
+
/** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index f6d9351952f1..4907d444070d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -39,4 +39,7 @@ class NotificationViewHeightRepository @Inject constructor() {
* consumed part of the gesture.
*/
val isCurrentGestureOverscroll = MutableStateFlow(false)
+
+ /** Whether the current touch gesture is on any open notification guts. */
+ val isCurrentGestureInGuts = MutableStateFlow(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 8557afc6ebd3..756cd87970a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository
@@ -39,6 +40,7 @@ class NotificationStackAppearanceInteractor
constructor(
private val viewHeightRepository: NotificationViewHeightRepository,
private val placeholderRepository: NotificationPlaceholderRepository,
+ sceneInteractor: SceneInteractor,
shadeInteractor: ShadeInteractor,
) {
/** The bounds of the notification stack in the current scene. */
@@ -93,6 +95,15 @@ constructor(
val isCurrentGestureOverscroll: Flow<Boolean> =
viewHeightRepository.isCurrentGestureOverscroll.asStateFlow()
+ /** Whether we should close any notification guts that are currently open. */
+ val shouldCloseGuts: Flow<Boolean> =
+ combine(
+ sceneInteractor.isSceneContainerUserInputOngoing,
+ viewHeightRepository.isCurrentGestureInGuts
+ ) { isUserInputOngoing, isCurrentGestureInGuts ->
+ isUserInputOngoing && !isCurrentGestureInGuts
+ }
+
/** Sets the alpha to apply to the NSSL for the brightness mirror */
fun setAlphaForBrightnessMirror(alpha: Float) {
placeholderRepository.alphaForBrightnessMirror.value = alpha
@@ -119,6 +130,10 @@ constructor(
viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll
}
+ fun setCurrentGestureInGuts(isInGuts: Boolean) {
+ viewHeightRepository.isCurrentGestureInGuts.value = isInGuts
+ }
+
fun setConstrainedAvailableSpace(height: Int) {
placeholderRepository.constrainedAvailableSpace.value = height
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 1289cec3a282..235b4da3f029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -71,6 +71,9 @@ interface NotificationScrollView {
/** Set a consumer for current gesture overscroll events */
fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?)
+ /** Set a consumer for current gesture in guts events */
+ fun setCurrentGestureInGutsConsumer(consumer: Consumer<Boolean>?)
+
/** Set a consumer for heads up height changed events */
fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
@@ -92,6 +95,12 @@ interface NotificationScrollView {
/** Gets the inset for HUNs when they are not visible */
fun getHeadsUpInset(): Int
+ /**
+ * Signals that any open Notification guts should be closed, as scene container is handling
+ * touch events.
+ */
+ fun closeGutsOnSceneTouch()
+
/** Adds a listener to be notified, when the stack height might have changed. */
fun addStackHeightChangedListener(runnable: Runnable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index c044f6f6a9b1..3cc6e81986c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -36,6 +36,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
/** Binds the [NotificationScrollView]. */
@@ -98,13 +99,18 @@ constructor(
.filter { it }
.collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
}
+ launch {
+ viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
+ }
launchAndDispose {
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+ view.setCurrentGestureInGutsConsumer(viewModel.currentGestureInGutsConsumer)
DisposableHandle {
view.setSyntheticScrollConsumer(null)
view.setCurrentGestureOverscrollConsumer(null)
+ view.setCurrentGestureInGutsConsumer(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index b2045fe7569a..3999578b83e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -136,6 +136,9 @@ constructor(
val qsExpandFraction: Flow<Float> =
shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
+ /** Whether we should close any open notification guts. */
+ val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts
+
val shouldResetStackTop: Flow<Boolean> =
sceneInteractor.transitionState
.mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
@@ -202,6 +205,10 @@ constructor(
val currentGestureOverscrollConsumer: (Boolean) -> Unit =
stackAppearanceInteractor::setCurrentGestureOverscroll
+ /** Receives whether the current touch gesture is inside any open guts. */
+ val currentGestureInGutsConsumer: (Boolean) -> Unit =
+ stackAppearanceInteractor::setCurrentGestureInGuts
+
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
sceneInteractor.currentScene
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0f93ff2b70ed..f11fd7b29c18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -68,7 +68,6 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
@@ -104,6 +103,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Lazy;
@@ -179,6 +179,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private float mFraction = -1f;
private boolean mTracking = false;
private boolean mBouncerShowingOverDream;
+ private int mAttemptsToShowBouncer = 0;
+ private DelayableExecutor mExecutor;
private final PrimaryBouncerExpansionCallback mExpansionCallback =
new PrimaryBouncerExpansionCallback() {
@@ -315,8 +317,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastScreenOffAnimationPlaying;
private float mQsExpansion;
- private FeatureFlags mFlags;
-
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
private boolean mIsBackAnimationEnabled;
private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@@ -399,9 +399,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
JavaAdapter javaAdapter,
Lazy<SceneInteractor> sceneInteractorLazy,
StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor,
+ @Main DelayableExecutor executor,
Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy
) {
mContext = context;
+ mExecutor = executor;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
mConfigurationController = configurationController;
@@ -711,13 +713,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* {@link #needsFullscreenBouncer()}.
*/
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
- boolean isDozing = mDozing;
- if (Flags.simPinRaceConditionOnRestart()) {
- KeyguardState toState = mKeyguardTransitionInteractor.getTransitionState().getValue()
- .getTo();
- isDozing = mDozing || toState == KeyguardState.DOZING || toState == KeyguardState.AOD;
- }
- if (needsFullscreenBouncer() && !isDozing) {
+ if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
if (!primaryBouncerIsShowing()) {
if (SceneContainerFlag.isEnabled()) {
@@ -727,9 +723,22 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
if (Flags.simPinRaceConditionOnRestart()) {
if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) {
+ mAttemptsToShowBouncer = 0;
mCentralSurfaces.hideKeyguard();
} else {
- mCentralSurfaces.showKeyguard();
+ if (mAttemptsToShowBouncer > 6) {
+ mAttemptsToShowBouncer = 0;
+ Log.e(TAG, "Too many failed attempts to show bouncer, showing "
+ + "keyguard instead");
+ mCentralSurfaces.showKeyguard();
+ } else {
+ Log.v(TAG, "Failed to show bouncer, attempt #: "
+ + mAttemptsToShowBouncer++);
+ mExecutor.executeDelayed(() ->
+ showBouncerOrKeyguard(hideBouncerWhenShowing,
+ isFalsingReset),
+ 500);
+ }
}
} else {
mCentralSurfaces.hideKeyguard();
@@ -1874,6 +1883,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
+ @VisibleForTesting
+ void setAttemptsToShowBouncer(int attempts) {
+ mAttemptsToShowBouncer = attempts;
+ }
+
/**
* Delegate used to send show and hide events to an alternate authentication method instead of
* the regular pin/pattern/password bouncer.
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 59c819d41493..cd32718dbe82 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -3,8 +3,6 @@ package com.android.systemui.user.domain.interactor
import android.annotation.UserIdInt
import android.content.pm.UserInfo
import android.os.UserManager
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags.refactorGetCurrentUser
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
@@ -21,23 +19,11 @@ class SelectedUserInteractor @Inject constructor(private val repository: UserRep
/** Flow providing the [UserInfo] of the currently selected user. */
val selectedUserInfo = repository.selectedUserInfo
- /**
- * Returns the ID of the currently-selected user.
- *
- * @param bypassFlag this will ignore the feature flag and get the data from the repository
- * instead. This is used for refactored methods that were previously pointing to `userTracker`
- * and therefore should not be routed back to KeyguardUpdateMonitor when flag is disabled.
- * KeyguardUpdateMonitor.getCurrentUser() is deprecated and will be removed soon (together
- * with this flag).
- */
+ /** Returns the ID of the currently-selected user. */
@UserIdInt
@JvmOverloads
- fun getSelectedUserId(bypassFlag: Boolean = false): Int {
- return if (bypassFlag || refactorGetCurrentUser()) {
- repository.getSelectedUserInfo().id
- } else {
- KeyguardUpdateMonitor.getCurrentUser()
- }
+ fun getSelectedUserId(): Int {
+ return repository.getSelectedUserInfo().id
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7aa415b64316..52fde7ed72c9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -354,7 +354,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mCurrentUserId);
- when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mCurrentUserId);
mContext.getOrCreateTestableResources().addOverride(
com.android.systemui.res.R.integer.config_face_auth_supported_posture,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
new file mode 100644
index 000000000000..ba990efd5162
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test for {@link AccessibilityGestureTargetsObserver}. */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class AccessibilityGestureTargetsObserverTest extends SysuiTestCase {
+ private static final int MY_USER_ID = ActivityManager.getCurrentUser();
+
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private AccessibilityGestureTargetsObserver.TargetsChangedListener mListener;
+
+ private AccessibilityGestureTargetsObserver mAccessibilityGestureTargetsObserver;
+
+ private static final String TEST_A11Y_BTN_TARGETS = "Magnification";
+
+ @Before
+ public void setUp() {
+ when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
+ mAccessibilityGestureTargetsObserver = new AccessibilityGestureTargetsObserver(mContext,
+ mUserTracker);
+ }
+
+ @Test
+ public void onChange_haveListener_invokeCallback() {
+ mAccessibilityGestureTargetsObserver.addListener(mListener);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+ MY_USER_ID);
+
+ mAccessibilityGestureTargetsObserver.mContentObserver.onChange(false);
+
+ verify(mListener).onAccessibilityGestureTargetsChanged(TEST_A11Y_BTN_TARGETS);
+ }
+
+ @Test
+ public void onChange_listenerRemoved_noInvokeCallback() {
+ mAccessibilityGestureTargetsObserver.addListener(mListener);
+ mAccessibilityGestureTargetsObserver.removeListener(mListener);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+ MY_USER_ID);
+
+ mAccessibilityGestureTargetsObserver.mContentObserver.onChange(false);
+
+ verify(mListener, never()).onAccessibilityGestureTargetsChanged(anyString());
+ }
+
+ @Test
+ public void getCurrentAccessibilityGestureTargets_expectedValue() {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+ MY_USER_ID);
+
+ final String actualValue =
+ mAccessibilityGestureTargetsObserver.getCurrentAccessibilityGestureTargets();
+
+ assertThat(actualValue).isEqualTo(TEST_A11Y_BTN_TARGETS);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 37f1a3d73b0c..597ffef20ace 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,7 +26,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
-import static com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER;
import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -250,7 +249,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
when(mCommunalTransitionViewModel.getTransitionFromOccludedEnded())
.thenReturn(mock(Flow.class));
when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mDefaultUserId);
- when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mDefaultUserId);
when(mProcessWrapper.isSystemUser()).thenReturn(true);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
@@ -275,7 +273,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mKosmos.getNotificationShadeWindowModel(),
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
- mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
mSetFlagsRule.disableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR);
DejankUtils.setImmediate(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index a0fe538bdd2b..3cbbb648af94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
@@ -62,26 +63,19 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val testScope = kosmos.testScope
- private lateinit var dismissInteractorWithDependencies:
- KeyguardDismissInteractorFactory.WithDependencies
+ private lateinit var dismissInteractor: KeyguardDismissInteractor
private lateinit var underTest: KeyguardDismissActionInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- dismissInteractorWithDependencies =
- KeyguardDismissInteractorFactory.create(
- context = context,
- testScope = testScope,
- keyguardRepository = keyguardRepository,
- )
-
+ dismissInteractor = kosmos.keyguardDismissInteractor
underTest =
KeyguardDismissActionInteractor(
repository = keyguardRepository,
transitionInteractor = kosmos.keyguardTransitionInteractor,
- dismissInteractor = dismissInteractorWithDependencies.interactor,
+ dismissInteractor = dismissInteractor,
applicationScope = testScope.backgroundScope,
sceneInteractor = kosmos.sceneInteractor,
deviceEntryInteractor = kosmos.deviceEntryInteractor,
@@ -166,9 +160,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
willAnimateOnLockscreen = true,
)
)
- dismissInteractorWithDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(
- true
- )
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
assertThat(executeDismissAction).isEqualTo(onDismissAction)
}
@@ -307,8 +299,7 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
@Test
fun setKeyguardDone() =
testScope.runTest {
- val keyguardDoneTiming by
- collectLastValue(dismissInteractorWithDependencies.interactor.keyguardDone)
+ val keyguardDoneTiming by collectLastValue(dismissInteractor.keyguardDone)
runCurrent()
underTest.setKeyguardDone(KeyguardDone.LATER)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index ecb46bdd06c4..fabed03bc18c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -23,11 +23,18 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.TrustGrantFlags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeTrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.TrustModel
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -38,14 +45,16 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardDismissInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var dispatcher: TestDispatcher
private lateinit var testScope: TestScope
- private lateinit var underTestDependencies: KeyguardDismissInteractorFactory.WithDependencies
- private lateinit var underTest: KeyguardDismissInteractor
+ private val underTest = kosmos.keyguardDismissInteractor
private val userInfo = UserInfo(0, "", 0)
@Before
@@ -54,13 +63,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
dispatcher = StandardTestDispatcher()
testScope = TestScope(dispatcher)
- underTestDependencies =
- KeyguardDismissInteractorFactory.create(
- context = context,
- testScope = testScope,
- )
- underTest = underTestDependencies.interactor
- underTestDependencies.userRepository.setUserInfos(listOf(userInfo))
+ kosmos.fakeUserRepository.setUserInfos(listOf(userInfo))
}
@Test
@@ -69,10 +72,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
val dismissKeyguardRequestWithoutImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
- underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(null)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
- underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(true)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
}
@@ -81,7 +84,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
testScope.runTest {
val dismissKeyguardRequestWithoutImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
- underTestDependencies.trustRepository.setRequestDismissKeyguard(
+ kosmos.fakeTrustRepository.setRequestDismissKeyguard(
TrustModel(
true,
0,
@@ -90,8 +93,8 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
- underTestDependencies.powerRepository.setInteractive(true)
- underTestDependencies.trustRepository.setRequestDismissKeyguard(
+ kosmos.fakePowerRepository.setInteractive(true)
+ kosmos.fakeTrustRepository.setRequestDismissKeyguard(
TrustModel(
true,
0,
@@ -106,15 +109,15 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
testScope.runTest {
val dismissKeyguardRequestWithoutImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
- underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+ kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
runCurrent()
// authenticated different user
- underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
// authenticated correct user
- underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
}
@@ -123,17 +126,15 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
testScope.runTest {
val dismissKeyguardRequestWithoutImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
- underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+ kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
runCurrent()
// requested from different user
- underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
- 22
- )
+ kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(22)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
// requested from correct user
- underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+ kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
userInfo.id
)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
@@ -159,10 +160,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
val dismissKeyguardRequestWithImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
- underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+ kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
runCurrent()
- underTestDependencies.keyguardRepository.setDismissAction(
+ kosmos.fakeKeyguardRepository.setDismissAction(
DismissAction.RunImmediately(
onDismissAction = { KeyguardDone.IMMEDIATE },
onCancelAction = {},
@@ -170,7 +171,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
willAnimateOnLockscreen = true,
)
)
- underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+ kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
userInfo.id
)
assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
@@ -184,10 +185,10 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
val dismissKeyguardRequestWithImmediateDismissAction by
collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
- underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+ kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
runCurrent()
- underTestDependencies.keyguardRepository.setDismissAction(
+ kosmos.fakeKeyguardRepository.setDismissAction(
DismissAction.RunAfterKeyguardGone(
dismissAction = {},
onCancelAction = {},
@@ -195,7 +196,7 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
willAnimateOnLockscreen = true,
)
)
- underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+ kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
userInfo.id
)
assertThat(dismissKeyguardRequestWithImmediateDismissAction).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 2cc8cc7d7896..99c5b7cdfdc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.data.repository.MediaDataRepository
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
@@ -134,6 +135,7 @@ private fun <T> anyObject(): T {
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class MediaDataProcessorTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -200,8 +202,6 @@ class MediaDataProcessorTest : SysuiTestCase() {
@Before
fun setup() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
staticMockSession =
ExtendedMockito.mockitoSession()
.mockStatic<UriGrantsManager>(UriGrantsManager::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index f8358c51ed5c..850916be35bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -186,7 +186,6 @@ class MediaCarouselControllerTest : SysuiTestCase() {
sceneInteractor = kosmos.sceneInteractor,
)
verify(configurationController).addCallback(capture(configListener))
- verify(mediaDataManager).addListener(capture(listener))
verify(visualStabilityProvider)
.addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCallback))
@@ -405,8 +404,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
}
+ @DisableSceneContainer
@Test
fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
+ verify(mediaDataManager).addListener(capture(listener))
+
testPlayerOrdering()
// If smartspace is prioritized
@@ -439,8 +441,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
}
+ @DisableSceneContainer
@Test
fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
+ verify(mediaDataManager).addListener(capture(listener))
+
testPlayerOrdering()
// playing paused player
listener.value.onMediaDataLoaded(
@@ -547,8 +552,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
+ @DisableSceneContainer
@Test
fun testMediaLoaded_ScrollToActivePlayer() {
+ verify(mediaDataManager).addListener(capture(listener))
+
listener.value.onMediaDataLoaded(
PLAYING_LOCAL,
null,
@@ -604,8 +612,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
)
}
+ @DisableSceneContainer
@Test
fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
+ verify(mediaDataManager).addListener(capture(listener))
+
listener.value.onSmartspaceMediaDataLoaded(
SMARTSPACE_KEY,
EMPTY_SMARTSPACE_MEDIA_DATA.copy(packageName = "PACKAGE_NAME", isActive = true),
@@ -647,8 +658,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
assertEquals(playerIndex, 0)
}
+ @DisableSceneContainer
@Test
fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
+ verify(mediaDataManager).addListener(capture(listener))
+
var result = false
mediaCarouselController.updateHostVisibility = { result = true }
@@ -658,8 +672,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
assertEquals(true, result)
}
+ @DisableSceneContainer
@Test
fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
+ verify(mediaDataManager).addListener(capture(listener))
+
var result = false
mediaCarouselController.updateHostVisibility = { result = true }
@@ -788,8 +805,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(pageIndicator, times(4)).setNumPages(any())
}
+ @DisableSceneContainer
@Test
fun testRecommendation_persistentEnabled_newSmartspaceLoaded_updatesSort() {
+ verify(mediaDataManager).addListener(capture(listener))
+
testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded()
// When an update to existing smartspace data is loaded
@@ -804,8 +824,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
}
+ @DisableSceneContainer
@Test
fun testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded() {
+ verify(mediaDataManager).addListener(capture(listener))
+
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
// When inactive smartspace data is loaded
@@ -1023,11 +1046,13 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(panel).updateAnimatorDurationScale()
}
+ @DisableSceneContainer
@Test
fun swipeToDismiss_pausedAndResumeOff_userInitiated() {
+ verify(mediaDataManager).addListener(capture(listener))
+
// When resumption is disabled, paused media should be dismissed after being swiped away
Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
-
val pausedMedia = DATA.copy(isPlaying = false)
listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
mediaCarouselController.onSwipeToDismiss()
@@ -1042,8 +1067,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
}
+ @DisableSceneContainer
@Test
fun swipeToDismiss_pausedAndResumeOff_delayed_userInitiated() {
+ verify(mediaDataManager).addListener(capture(listener))
+
// When resumption is disabled, paused media should be dismissed after being swiped away
Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
mediaCarouselController.updateHostVisibility = {}
@@ -1068,6 +1096,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
* @param function called when a certain configuration change occurs.
*/
private fun testConfigurationChange(function: () -> Unit) {
+ verify(mediaDataManager).addListener(capture(listener))
mediaCarouselController.pageIndicator = pageIndicator
listener.value.onMediaDataLoaded(
PLAYING_LOCAL,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 521aa5a7352b..1260a65b9c1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -70,6 +70,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -84,7 +85,6 @@ import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.monet.ColorScheme
@@ -141,6 +141,7 @@ private const val APP_NAME = "APP_NAME"
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@DisableSceneContainer
public class MediaControlPanelTest : SysuiTestCase() {
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@@ -233,9 +234,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var recProgressBar1: SeekBar
@Mock private lateinit var recProgressBar2: SeekBar
@Mock private lateinit var recProgressBar3: SeekBar
- private var shouldShowBroadcastButton: Boolean = false
@Mock private lateinit var globalSettings: GlobalSettings
- @Mock private lateinit var mediaFlags: MediaFlags
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -254,7 +253,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
.thenReturn(applicationInfo)
whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
context.setMockPackageManager(packageManager)
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(false)
player =
object :
@@ -278,7 +276,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
lockscreenUserManager,
broadcastDialogController,
globalSettings,
- mediaFlags,
) {
override fun loadAnimator(
animId: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 6c350cb4a5b0..2370bca52951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -40,7 +41,6 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaHostState
-import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
@@ -85,6 +85,7 @@ import org.mockito.kotlin.anyOrNull
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@DisableSceneContainer
class MediaHierarchyManagerTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -105,7 +106,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
@Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var logger: MediaViewLogger
- @Mock private lateinit var mediaFlags: MediaFlags
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -139,7 +139,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
shadeExpansion = MutableStateFlow(0f)
whenever(shadeInteractor.isQsBypassingShade).thenReturn(isQsBypassingShade)
whenever(shadeInteractor.shadeExpansion).thenReturn(shadeExpansion)
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(false)
mediaHierarchyManager =
MediaHierarchyManager(
context,
@@ -160,7 +159,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
testScope.backgroundScope,
ResourcesSplitShadeStateController(),
logger,
- mediaFlags,
)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index 00b9a46f340a..e765b6f77155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -38,12 +38,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.CachingIconView
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -113,7 +113,6 @@ class MediaViewControllerTest : SysuiTestCase() {
@Mock private lateinit var mediaTitleWidgetState: WidgetState
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
@Mock private lateinit var mediaContainerWidgetState: WidgetState
- @Mock private lateinit var mediaFlags: MediaFlags
@Mock private lateinit var seekBarViewModel: SeekBarViewModel
@Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
@Mock private lateinit var globalSettings: GlobalSettings
@@ -140,7 +139,6 @@ class MediaViewControllerTest : SysuiTestCase() {
logger,
seekBarViewModel,
mainExecutor,
- mediaFlags,
globalSettings,
) {
override fun loadAnimator(
@@ -374,10 +372,9 @@ class MediaViewControllerTest : SysuiTestCase() {
verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
}
+ @EnableSceneContainer
@Test
fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
getEnabledChangeListener().onEnabledChanged(enabled = true)
getEnabledChangeListener().onEnabledChanged(enabled = false)
@@ -386,10 +383,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.INVISIBLE)
}
+ @EnableSceneContainer
@Test
fun attachPlayer_seekBarEnabled_seekBarVisible() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
getEnabledChangeListener().onEnabledChanged(enabled = true)
@@ -397,10 +393,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.VISIBLE)
}
+ @EnableSceneContainer
@Test
fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
getEnabledChangeListener().onEnabledChanged(enabled = true)
@@ -413,10 +408,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.INVISIBLE)
}
+ @EnableSceneContainer
@Test
fun attachPlayer_notScrubbing_scrubbingViewsGone() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.canShowScrubbingTime = true
getScrubbingChangeListener().onScrubbingChanged(true)
@@ -433,10 +427,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.GONE)
}
+ @EnableSceneContainer
@Test
fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.canShowScrubbingTime = false
getScrubbingChangeListener().onScrubbingChanged(true)
@@ -452,10 +445,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.GONE)
}
+ @EnableSceneContainer
@Test
fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.setUpNextButtonInfo(true)
mediaViewController.setUpPrevButtonInfo(false)
@@ -474,10 +466,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.GONE)
}
+ @EnableSceneContainer
@Test
fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.setUpNextButtonInfo(false)
mediaViewController.setUpPrevButtonInfo(true)
@@ -496,10 +487,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.GONE)
}
+ @EnableSceneContainer
@Test
fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.setUpNextButtonInfo(true)
mediaViewController.setUpPrevButtonInfo(true)
@@ -522,10 +512,9 @@ class MediaViewControllerTest : SysuiTestCase() {
.isEqualTo(ConstraintSet.VISIBLE)
}
+ @EnableSceneContainer
@Test
fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() {
- whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
mediaViewController.attachPlayer(viewHolder)
mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE)
mediaViewController.setUpPrevButtonInfo(true, ConstraintSet.INVISIBLE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index bfbb7ceee6b5..a770ee199ba6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -18,7 +18,9 @@ package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -37,6 +39,9 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.provider.Flags;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -47,6 +52,7 @@ import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutT
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
@@ -94,6 +100,8 @@ public class NavBarHelperTest extends SysuiTestCase {
@Mock
AccessibilityButtonTargetsObserver mAccessibilityButtonTargetObserver;
@Mock
+ AccessibilityGestureTargetsObserver mAccessibilityGestureTargetObserver;
+ @Mock
SystemActions mSystemActions;
@Mock
OverviewProxyService mOverviewProxyService;
@@ -152,6 +160,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mAccessibilityManager).addAccessibilityServicesStateChangeListener(any());
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
+ mAccessibilityGestureTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
() -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
@@ -171,6 +180,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
verify(mAccessibilityButtonTargetObserver, times(1)).addListener(mNavBarHelper);
+ verify(mAccessibilityGestureTargetObserver, times(1)).addListener(mNavBarHelper);
verify(mAccessibilityManager, times(1)).addAccessibilityServicesStateChangeListener(
mNavBarHelper);
verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
@@ -185,6 +195,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
verify(mAccessibilityButtonModeObserver, times(1)).removeListener(mNavBarHelper);
verify(mAccessibilityButtonTargetObserver, times(1)).removeListener(mNavBarHelper);
+ verify(mAccessibilityGestureTargetObserver, times(1)).removeListener(mNavBarHelper);
verify(mAccessibilityManager, times(1)).removeAccessibilityServicesStateChangeListener(
mNavBarHelper);
verify(mWm, times(1)).removeRotationWatcher(any());
@@ -353,6 +364,83 @@ public class NavBarHelperTest extends SysuiTestCase {
verify(mEdgeBackGestureHandler, times(1)).onConfigurationChanged(any());
}
+ @Test
+ public void updateA11yState_navBarMode_softwareTargets_isClickable() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+ .thenReturn(createFakeShortcutTargets());
+
+ mNavBarHelper.updateA11yState();
+ long state = mNavBarHelper.getA11yButtonState();
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void updateA11yState_gestureMode_softwareTargets_isClickable() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_GESTURE);
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+ .thenReturn(createFakeShortcutTargets());
+
+ mNavBarHelper.updateA11yState();
+ long state = mNavBarHelper.getA11yButtonState();
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void updateA11yState_gestureNavMode_floatingButtonMode_gestureTargets_isClickable() {
+ mNavBarHelper.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.GESTURE))
+ .thenReturn(createFakeShortcutTargets());
+
+ mNavBarHelper.updateA11yState();
+ long state = mNavBarHelper.getA11yButtonState();
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void updateA11yState_navBarMode_gestureTargets_isNotClickable() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.GESTURE))
+ .thenReturn(createFakeShortcutTargets());
+
+ mNavBarHelper.updateA11yState();
+ long state = mNavBarHelper.getA11yButtonState();
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(0);
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(0);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+ public void updateA11yState_singleTarget_clickableButNotLongClickable() {
+ when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+ .thenReturn(new ArrayList<>(List.of("a")));
+
+ mNavBarHelper.updateA11yState();
+ long state = mNavBarHelper.getA11yButtonState();
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+ SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+ assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(0);
+ }
+
private List<String> createFakeShortcutTargets() {
return new ArrayList<>(List.of("a", "b", "c", "d"));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 04d140c458e8..2905a7329d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -85,6 +85,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
@@ -287,6 +288,7 @@ public class NavigationBarTest extends SysuiTestCase {
mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
mock(AccessibilityButtonModeObserver.class),
mock(AccessibilityButtonTargetsObserver.class),
+ mock(AccessibilityGestureTargetsObserver.class),
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
mKeyguardStateController, mock(NavigationModeController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index ebab04989590..748c7d9d939b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -497,6 +497,17 @@ public class QSTileImplTest extends SysuiTestCase {
assertThat(mTile.mRefreshes).isEqualTo(1);
}
+ @Test
+ public void testStaleTriggeredOnUserSwitch() {
+ mTile.clearRefreshes();
+
+ mTile.userSwitch(10);
+ mTestableLooper.processAllMessages();
+
+ assertFalse(mTile.isListening());
+ assertThat(mTile.mRefreshes).isEqualTo(1);
+ }
+
private void assertEvent(UiEventLogger.UiEventEnum eventType,
UiEventLoggerFake.FakeUiEvent fakeEvent) {
assertEquals(eventType.getId(), fakeEvent.eventId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 90c70f26e26b..5a5cdcd99054 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,7 +33,6 @@ import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BACK_GESTURE
import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
@@ -442,7 +441,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit() =
with(kosmos) {
testScope.runTest {
@@ -468,7 +467,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
@EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_fullSwipe() =
with(kosmos) {
@@ -489,7 +487,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+ @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_rtl() =
with(kosmos) {
testScope.runTest {
@@ -514,7 +512,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
}
- @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
@EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun gestureExclusionZone_setAfterInit_rtl_fullSwipe() =
with(kosmos) {
@@ -535,102 +532,6 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
- @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun gestureExclusionZone_setAfterInit_backGestureEnabled() =
- with(kosmos) {
- testScope.runTest {
- whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
- goToScene(CommunalScenes.Communal)
-
- assertThat(containerView.systemGestureExclusionRects)
- .containsExactly(
- Rect(
- /* left= */ FAKE_INSETS.left,
- /* top= */ TOP_SWIPE_REGION_WIDTH,
- /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right,
- /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
- ),
- Rect(
- /* left= */ 0,
- /* top= */ 0,
- /* right= */ FAKE_INSETS.right,
- /* bottom= */ CONTAINER_HEIGHT
- )
- )
- }
- }
-
- @Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun gestureExclusionZone_setAfterInit_backGestureEnabled_fullSwipe() =
- with(kosmos) {
- testScope.runTest {
- whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
- goToScene(CommunalScenes.Communal)
-
- assertThat(containerView.systemGestureExclusionRects)
- .containsExactly(
- Rect(
- /* left= */ 0,
- /* top= */ 0,
- /* right= */ FAKE_INSETS.right,
- /* bottom= */ CONTAINER_HEIGHT
- )
- )
- }
- }
-
- @Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
- @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl() =
- with(kosmos) {
- testScope.runTest {
- whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
- goToScene(CommunalScenes.Communal)
-
- assertThat(containerView.systemGestureExclusionRects)
- .containsExactly(
- Rect(
- /* left= */ FAKE_INSETS.left,
- /* top= */ TOP_SWIPE_REGION_WIDTH,
- /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right,
- /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
- ),
- Rect(
- /* left= */ FAKE_INSETS.left,
- /* top= */ 0,
- /* right= */ CONTAINER_WIDTH,
- /* bottom= */ CONTAINER_HEIGHT
- )
- )
- }
- }
-
- @Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE, FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl_fullSwipe() =
- with(kosmos) {
- testScope.runTest {
- whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
- goToScene(CommunalScenes.Communal)
-
- assertThat(containerView.systemGestureExclusionRects)
- .containsExactly(
- Rect(
- Rect(
- /* left= */ FAKE_INSETS.left,
- /* top= */ 0,
- /* right= */ CONTAINER_WIDTH,
- /* bottom= */ CONTAINER_HEIGHT
- )
- )
- )
- }
- }
-
- @Test
fun gestureExclusionZone_unsetWhenShadeOpen() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 9b611057c059..b75ac2bc9bde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -108,7 +108,9 @@ import com.android.systemui.statusbar.domain.interactor.StatusBarKeyguardViewMan
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.time.FakeSystemClock;
import com.google.common.truth.Truth;
@@ -175,6 +177,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBouncerExpansionCallback;
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
private ViewRootImpl mViewRootImpl;
@@ -238,6 +241,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(JavaAdapter.class),
() -> mSceneInteractor,
mock(StatusBarKeyguardViewManagerInteractor.class),
+ mExecutor,
() -> mDeviceEntryInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
@@ -760,6 +764,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(JavaAdapter.class),
() -> mSceneInteractor,
mock(StatusBarKeyguardViewManagerInteractor.class),
+ mExecutor,
() -> mDeviceEntryInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
@@ -1084,6 +1089,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(KeyguardState.LOCKSCREEN);
reset(mCentralSurfaces);
+ // Advance past reattempts
+ mStatusBarKeyguardViewManager.setAttemptsToShowBouncer(10);
+
mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
verify(mPrimaryBouncerInteractor).show(true);
verify(mCentralSurfaces).showKeyguard();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
index 230ddf9d25db..48c2cc7f11c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
@@ -56,11 +56,11 @@ class StatusBarTouchableRegionManagerTest : SysuiTestCase() {
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
- sceneRepository.isRemoteUserInteractionOngoing.value = true
+ sceneRepository.isRemoteUserInputOngoing.value = true
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue()
- sceneRepository.isRemoteUserInteractionOngoing.value = false
+ sceneRepository.isRemoteUserInputOngoing.value = false
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
}
@@ -71,7 +71,7 @@ class StatusBarTouchableRegionManagerTest : SysuiTestCase() {
testScope.runTest {
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
- sceneRepository.isRemoteUserInteractionOngoing.value = true
+ sceneRepository.isRemoteUserInputOngoing.value = true
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
index 78028f819fa0..26f6f3131585 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -3,7 +3,6 @@ package com.android.systemui.user.domain.interactor
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER
import com.android.systemui.SysuiTestCase
import com.android.systemui.user.data.repository.FakeUserRepository
import com.google.common.truth.Truth.assertThat
@@ -28,7 +27,6 @@ class SelectedUserInteractorTest : SysuiTestCase() {
@Test
fun getSelectedUserIdReturnsId() {
- mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER)
runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
val actualId = underTest.getSelectedUserId()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
deleted file mode 100644
index 9b7bca6d2d34..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import android.content.Context
-import android.os.Handler
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.bouncer.ui.BouncerView
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.keyguard.DismissCallbackRegistry
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.time.FakeSystemClock
-import kotlinx.coroutines.test.TestScope
-import org.mockito.Mockito.mock
-
-/**
- * Helper to create a new KeyguardDismissInteractor in a way that doesn't require modifying many
- * tests whenever we add a constructor param.
- */
-object KeyguardDismissInteractorFactory {
- @JvmOverloads
- @JvmStatic
- fun create(
- context: Context,
- testScope: TestScope,
- trustRepository: FakeTrustRepository = FakeTrustRepository(),
- keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
- bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
- keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java),
- powerRepository: FakePowerRepository = FakePowerRepository(),
- userRepository: FakeUserRepository = FakeUserRepository(),
- ): WithDependencies {
- val primaryBouncerInteractor =
- PrimaryBouncerInteractor(
- bouncerRepository,
- mock(BouncerView::class.java),
- mock(Handler::class.java),
- mock(KeyguardStateController::class.java),
- mock(KeyguardSecurityModel::class.java),
- mock(PrimaryBouncerCallbackInteractor::class.java),
- mock(FalsingCollector::class.java),
- mock(DismissCallbackRegistry::class.java),
- context,
- keyguardUpdateMonitor,
- trustRepository,
- testScope.backgroundScope,
- mock(SelectedUserInteractor::class.java),
- mock(DeviceEntryFaceAuthInteractor::class.java),
- )
- val alternateBouncerInteractor =
- AlternateBouncerInteractor(
- mock(StatusBarStateController::class.java),
- mock(KeyguardStateController::class.java),
- bouncerRepository,
- FakeFingerprintPropertyRepository(),
- FakeBiometricSettingsRepository(),
- FakeSystemClock(),
- keyguardUpdateMonitor,
- { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
- { mock(KeyguardInteractor::class.java) },
- { mock(KeyguardTransitionInteractor::class.java) },
- { mock(SceneInteractor::class.java) },
- testScope.backgroundScope,
- )
- val powerInteractorWithDeps =
- PowerInteractorFactory.create(
- repository = powerRepository,
- )
- val selectedUserInteractor = SelectedUserInteractor(repository = userRepository)
- return WithDependencies(
- trustRepository = trustRepository,
- keyguardRepository = keyguardRepository,
- bouncerRepository = bouncerRepository,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- powerRepository = powerRepository,
- userRepository = userRepository,
- interactor =
- KeyguardDismissInteractor(
- trustRepository,
- keyguardRepository,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- powerInteractorWithDeps.powerInteractor,
- selectedUserInteractor,
- ),
- )
- }
-
- data class WithDependencies(
- val trustRepository: FakeTrustRepository,
- val keyguardRepository: FakeKeyguardRepository,
- val bouncerRepository: FakeKeyguardBouncerRepository,
- val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- val powerRepository: FakePowerRepository,
- val userRepository: FakeUserRepository,
- val interactor: KeyguardDismissInteractor,
- )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
index f33ca95e488d..ace11573c7c6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -20,7 +20,10 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.trustRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -29,11 +32,14 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.keyguardDismissInteractor by
Kosmos.Fixture {
KeyguardDismissInteractor(
- trustRepository = trustRepository,
+ mainDispatcher = testDispatcher,
+ scope = applicationCoroutineScope,
keyguardRepository = keyguardRepository,
primaryBouncerInteractor = primaryBouncerInteractor,
+ selectedUserInteractor = selectedUserInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
+ trustRepository = trustRepository,
alternateBouncerInteractor = alternateBouncerInteractor,
powerInteractor = powerInteractor,
- selectedUserInteractor = selectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
index 6252d4498a5e..4b42e07f1f54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
@@ -24,6 +23,5 @@ val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
Kosmos.Fixture {
NotificationShadeWindowModel(
keyguardTransitionInteractor,
- keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
index dbfd9de2aa8c..2772d3698d88 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.data.repository.notificationPlaceholderRepository
import com.android.systemui.statusbar.notification.stack.data.repository.notificationViewHeightRepository
@@ -26,6 +27,7 @@ val Kosmos.notificationStackAppearanceInteractor by Fixture {
NotificationStackAppearanceInteractor(
viewHeightRepository = notificationViewHeightRepository,
placeholderRepository = notificationPlaceholderRepository,
+ sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
)
}
diff --git a/services/Android.bp b/services/Android.bp
index 0006455f41b0..653cd3c3b680 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -136,6 +136,7 @@ filegroup {
":services.searchui-sources",
":services.smartspace-sources",
":services.soundtrigger-sources",
+ ":services.supervision-sources",
":services.systemcaptions-sources",
":services.translation-sources",
":services.texttospeech-sources",
@@ -237,6 +238,7 @@ system_java_library {
"services.searchui",
"services.smartspace",
"services.soundtrigger",
+ "services.supervision",
"services.systemcaptions",
"services.translation",
"services.texttospeech",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 19279a887d11..07e5f2e34ab8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3527,7 +3527,8 @@ class StorageManagerService extends IStorageManager.Stub
// of the calling App
final long token = Binder.clearCallingIdentity();
try {
- Context targetAppContext = mContext.createPackageContext(packageName, 0);
+ Context targetAppContext = mContext.createPackageContextAsUser(packageName,
+ /* flags= */ 0, UserHandle.of(UserHandle.getUserId(originalUid)));
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(packageName,
appInfo.manageSpaceActivityName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b599a2fe64fe..d80b38e32b6c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12168,6 +12168,7 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(" -p: dump also private dirty memory usage.");
pw.println(" --oom: only show processes organized by oom adj.");
pw.println(" --local: only collect details locally, don't call process.");
+ pw.println(" --logstats: dump native allocator stats to log");
pw.println(" --package: interpret process arg as package, dumping all");
pw.println(" processes that have loaded that package.");
pw.println(" --checkin: dump data for a checkin");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
index cf677d541fb2..7b1186c9d4c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -80,13 +80,16 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @NonNull
+ private final FaceUtils mBiometricUtils;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
@NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback,
+ @NonNull FaceUtils biometricUtils) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
@@ -95,6 +98,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
+ mBiometricUtils = biometricUtils;
}
@Override
@@ -167,8 +171,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
} else {
currentUserId = client.getTargetUserId();
}
- final CharSequence name = FaceUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
+ final CharSequence name = mBiometricUtils.getUniqueName(mContext, currentUserId);
final Face face = new Face(name, enrollmentId, mSensorId);
handleResponse(FaceEnrollClient.class, (c) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 3eecc6de7450..d4ec573e1667 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -60,7 +60,6 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.face.FaceService;
-import com.android.server.biometrics.sensors.face.FaceUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -85,6 +84,7 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
private final int mMaxTemplatesPerUser;
private final boolean mDebugConsent;
private final @android.hardware.face.FaceEnrollOptions.EnrollReason int mEnrollReason;
+ private final BiometricUtils<Face> mBiometricUtils;
private final ClientMonitorCallback mPreviewHandleDeleterCallback =
new ClientMonitorCallback() {
@@ -107,7 +107,8 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
int maxTemplatesPerUser, boolean debugConsent,
android.hardware.face.FaceEnrollOptions options,
- @NonNull AuthenticationStateListeners authenticationStateListeners) {
+ @NonNull AuthenticationStateListeners authenticationStateListeners,
+ @NonNull BiometricUtils<Face> biometricUtils) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
@@ -122,6 +123,7 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
mDebugConsent = debugConsent;
mDisabledFeatures = disabledFeatures;
mPreviewSurface = previewSurface;
+ mBiometricUtils = biometricUtils;
Slog.w(TAG, "EnrollOptions "
+ android.hardware.face.FaceEnrollOptions.enrollReasonToString(
options.getEnrollReason()));
@@ -144,7 +146,7 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@Override
protected boolean hasReachedEnrollmentLimit() {
- return FaceUtils.getInstance(getSensorId()).getBiometricsForUser(getContext(),
+ return mBiometricUtils.getBiometricsForUser(getContext(),
getTargetUserId()).size() >= mMaxTemplatesPerUser;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index 964bf6cad63c..c27b7c483afc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -30,7 +30,6 @@ import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.InternalCleanupClient;
import com.android.server.biometrics.sensors.InternalEnumerateClient;
import com.android.server.biometrics.sensors.RemovalClient;
-import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.List;
import java.util.Map;
@@ -75,7 +74,7 @@ public class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlS
@Override
protected void onAddUnknownTemplate(int userId,
@NonNull BiometricAuthenticator.Identifier identifier) {
- FaceUtils.getInstance(getSensorId()).addBiometricForUser(
+ mBiometricUtils.addBiometricForUser(
getContext(), getTargetUserId(), (Face) identifier);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index f0a418951505..bb213bfa79e6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -72,7 +72,6 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
-import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSensorAdapter;
@@ -326,8 +325,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
if (Build.isDebuggable()) {
- BiometricUtils<Face> utils = FaceUtils.getInstance(
- mFaceSensors.keyAt(0));
+ BiometricUtils<Face> utils = mFaceSensors.get(
+ mFaceSensors.keyAt(0)).getFaceUtilsInstance();
for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
@@ -386,7 +385,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
new InvalidationRequesterClient<>(mContext, userId, sensorId,
BiometricLogger.ofUnknown(mContext),
mBiometricContext,
- FaceUtils.getInstance(sensorId));
+ mFaceSensors.get(sensorId).getFaceUtilsInstance());
scheduleForSensor(sensorId, client);
});
}
@@ -415,7 +414,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull
@Override
public List<Face> getEnrolledFaces(int sensorId, int userId) {
- return FaceUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
+ return mFaceSensors.get(sensorId).getFaceUtilsInstance()
+ .getBiometricsForUser(mContext, userId);
}
@Override
@@ -497,13 +497,14 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
+ opPackageName, id, mFaceSensors.get(sensorId).getFaceUtilsInstance(),
+ disabledFeatures, ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext, maxTemplatesPerUser, debugConsent, options,
- mAuthenticationStateListeners);
+ mAuthenticationStateListeners,
+ mFaceSensors.get(sensorId).getFaceUtilsInstance());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
return id;
@@ -615,7 +616,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
- final List<Face> faces = FaceUtils.getInstance(sensorId)
+ final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
.getBiometricsForUser(mContext, userId);
final int[] faceIds = new int[faces.size()];
for (int i = 0; i < faces.size(); i++) {
@@ -632,7 +633,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), faceIds, userId,
- opPackageName, FaceUtils.getInstance(sensorId), sensorId,
+ opPackageName, mFaceSensors.get(sensorId).getFaceUtilsInstance(), sensorId,
createLogger(BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
@@ -666,7 +667,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
- final List<Face> faces = FaceUtils.getInstance(sensorId)
+ final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(getTag(), "Ignoring setFeature, no templates enrolled for user: " + userId);
@@ -687,7 +688,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) {
mHandler.post(() -> {
mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
- final List<Face> faces = FaceUtils.getInstance(sensorId)
+ final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
.getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(getTag(), "Ignoring getFeature, no templates enrolled for user: " + userId);
@@ -727,7 +728,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
- FaceUtils.getInstance(sensorId),
+ mFaceSensors.get(sensorId).getFaceUtilsInstance(),
mFaceSensors.get(sensorId).getAuthenticatorIds());
if (favorHalEnrollments) {
client.setFavorHalEnrollments();
@@ -768,7 +769,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
- final int c = FaceUtils.getInstance(sensorId)
+ final int c = mFaceSensors.get(sensorId).getFaceUtilsInstance()
.getBiometricsForUser(mContext, userId).size();
JSONObject set = new JSONObject();
set.put("id", userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index b0e7575689ba..6f9534993a3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -158,7 +158,7 @@ public class Sensor {
Slog.e(TAG, "Face sensor hardware unavailable.");
mCurrentSession = null;
}
- });
+ }, getFaceUtilsInstance());
return Sensor.this.getStartUserClient(resultController, sensorId,
newUserId, provider);
@@ -280,8 +280,7 @@ public class Sensor {
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
proto.write(UserStateProto.NUM_ENROLLED,
- FaceUtils.getInstance(mSensorProperties.sensorId)
- .getBiometricsForUser(mContext, userId).size());
+ getFaceUtilsInstance().getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
@@ -358,4 +357,8 @@ public class Sensor {
Supplier<AidlSession> lazySession) {
mLazySession = lazySession;
}
+
+ public FaceUtils getFaceUtilsInstance() {
+ return FaceUtils.getInstance(mSensorProperties.sensorId);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
index 9a4c29d7e978..444a6d18d27f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -159,6 +159,11 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
}
@Override
+ public FaceUtils getFaceUtilsInstance() {
+ return FaceUtils.getLegacyInstance(getSensorProperties().sensorId);
+ }
+
+ @Override
protected LockoutTracker getLockoutTracker(boolean forAuth) {
return mLockoutTracker;
}
@@ -180,7 +185,8 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
mLockoutTracker,
mLockoutResetDispatcher,
mAuthSessionCoordinator,
- mAidlResponseHandlerCallback);
+ mAidlResponseHandlerCallback,
+ getFaceUtilsInstance());
}
private IBiometricsFace getIBiometricsFace() {
@@ -247,8 +253,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
return new FaceUpdateActiveUserClient(getContext(), this::getIBiometricsFace,
mUserStartedCallback, userId, TAG, getSensorProperties().sensorId,
BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
- !FaceUtils.getInstance(getSensorProperties().sensorId).getBiometricsForUser(
- getContext(), userId).isEmpty(),
+ !getFaceUtilsInstance().getBiometricsForUser(getContext(), userId).isEmpty(),
getAuthenticatorIds());
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
index 6d1715f1d500..80b7cde3124c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -80,13 +80,16 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
private final AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @NonNull
+ private final FingerprintUtils mBiometricUtils;
public AidlResponseHandler(@NonNull Context context,
@NonNull BiometricScheduler scheduler, int sensorId, int userId,
@NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+ @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback,
+ @NonNull FingerprintUtils biometricUtils) {
mContext = context;
mScheduler = scheduler;
mSensorId = sensorId;
@@ -95,6 +98,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
mLockoutResetDispatcher = lockoutResetDispatcher;
mAuthSessionCoordinator = authSessionCoordinator;
mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
+ mBiometricUtils = biometricUtils;
}
@Override
@@ -158,8 +162,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub {
} else {
currentUserId = client.getTargetUserId();
}
- final CharSequence name = FingerprintUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
+ final CharSequence name = mBiometricUtils.getUniqueName(mContext, currentUserId);
final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
enrollmentId, mSensorId);
handleResponse(FingerprintEnrollClient.class, (c) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 1fc517906c58..40b8a45beb36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -81,7 +81,7 @@ public class FingerprintInternalCleanupClient
@Override
protected void onAddUnknownTemplate(int userId,
@NonNull BiometricAuthenticator.Identifier identifier) {
- FingerprintUtils.getInstance(getSensorId()).addBiometricForUser(
+ mBiometricUtils.addBiometricForUser(
getContext(), getTargetUserId(), (Fingerprint) identifier);
}
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 12baf00c1c4a..9edaa4e6d818 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
@@ -79,7 +79,6 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorList;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
@@ -354,8 +353,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
if (Build.isDebuggable()) {
- BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
- mFingerprintSensors.keyAt(0));
+ final int sensorId = mFingerprintSensors.keyAt(0);
+ final BiometricUtils<Fingerprint> utils = mFingerprintSensors.get(sensorId)
+ .getFingerprintUtilsInstance();
for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
@@ -442,7 +442,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
new InvalidationRequesterClient<>(mContext, userId, sensorId,
BiometricLogger.ofUnknown(mContext),
mBiometricContext,
- FingerprintUtils.getInstance(sensorId));
+ mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance());
scheduleForSensor(sensorId, client);
});
}
@@ -507,7 +507,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FingerprintUtils.getInstance(sensorId),
+ opPackageName, mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
@@ -638,8 +638,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int userId,
@NonNull String opPackageName) {
- final List<Fingerprint> fingers = FingerprintUtils.getInstance(sensorId)
- .getBiometricsForUser(mContext, userId);
+ final List<Fingerprint> fingers = mFingerprintSensors.get(sensorId)
+ .getFingerprintUtilsInstance().getBiometricsForUser(mContext, userId);
final int[] fingerIds = new int[fingers.size()];
for (int i = 0; i < fingers.size(); i++) {
fingerIds[i] = fingers.get(i).getBiometricId();
@@ -655,11 +655,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
- opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext,
+ opPackageName, mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
+ sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext,
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
@@ -683,7 +682,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
mBiometricContext,
- FingerprintUtils.getInstance(sensorId),
+ mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
mFingerprintSensors.get(sensorId).getAuthenticatorIds());
if (favorHalEnrollments) {
client.setFavorHalEnrollments();
@@ -706,14 +705,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
- FingerprintUtils.getInstance(sensorId)
+ mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
.renameBiometricForUser(mContext, userId, fingerId, name);
}
@NonNull
@Override
public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
- return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
+ return mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
+ .getBiometricsForUser(mContext, userId);
}
@Override
@@ -842,7 +842,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
- final int c = FingerprintUtils.getInstance(sensorId)
+ final int c = mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
.getBiometricsForUser(mContext, userId).size();
JSONObject set = new JSONObject();
set.put("id", userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 1c6dfe0f5b24..d12d7b2dc89a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -170,7 +170,7 @@ public class Sensor {
"Fingerprint sensor hardware unavailable.");
mCurrentSession = null;
}
- });
+ }, getFingerprintUtilsInstance());
return Sensor.this.getStartUserClient(resultController, sensorId,
newUserId);
@@ -187,7 +187,7 @@ public class Sensor {
+ halInterfaceVersion);
mCurrentSession = new AidlSession(halInterfaceVersion,
newSession, userIdStarted, resultController);
- if (FingerprintUtils.getInstance(sensorId)
+ if (getFingerprintUtilsInstance()
.isInvalidationInProgress(mContext, userIdStarted)) {
Slog.w(TAG,
"Scheduling unfinished invalidation request for "
@@ -307,9 +307,8 @@ public class Sensor {
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED,
- FingerprintUtils.getInstance(mSensorProperties.sensorId)
- .getBiometricsForUser(mContext, userId).size());
+ proto.write(UserStateProto.NUM_ENROLLED, getFingerprintUtilsInstance()
+ .getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
@@ -386,4 +385,8 @@ public class Sensor {
public FingerprintProvider getProvider() {
return mProvider;
}
+
+ public FingerprintUtils getFingerprintUtilsInstance() {
+ return FingerprintUtils.getInstance(mSensorProperties.sensorId);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
index 3214b6d3363f..8f52d00ad830 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -148,6 +148,11 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
}
@Override
+ public FingerprintUtils getFingerprintUtilsInstance() {
+ return FingerprintUtils.getLegacyInstance(getSensorProperties().sensorId);
+ }
+
+ @Override
@Nullable
@VisibleForTesting
protected AidlSession getSessionForUser(int userId) {
@@ -186,7 +191,8 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
mLockoutTracker,
mLockoutResetDispatcher,
mAuthSessionCoordinator,
- mAidlResponseHandlerCallback);
+ mAidlResponseHandlerCallback,
+ getFingerprintUtilsInstance());
}
@VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() {
@@ -266,8 +272,7 @@ public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRe
() -> getSession().getSession(), newUserId, TAG,
getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()),
getBiometricContext(), () -> mCurrentUserId,
- !FingerprintUtils.getInstance(getSensorProperties().sensorId)
- .getBiometricsForUser(getContext(),
+ !getFingerprintUtilsInstance().getBiometricsForUser(getContext(),
newUserId).isEmpty(), getAuthenticatorIds(), forceUpdateAuthenticatorIds,
mUserStartedCallback);
}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index c31d1d8b271c..d909004e6381 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1500,10 +1500,18 @@ public class DisplayModeDirector {
}
private void updateLayoutLimitedFrameRate(int displayId, @Nullable DisplayInfo info) {
- Vote vote = info != null && info.layoutLimitedRefreshRate != null
- ? Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min,
- info.layoutLimitedRefreshRate.max) : null;
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote);
+ Vote refreshRateVote = null;
+ Vote frameRateVote = null;
+ if (info != null && info.layoutLimitedRefreshRate != null) {
+ refreshRateVote = Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min,
+ info.layoutLimitedRefreshRate.max);
+ frameRateVote = Vote.forRenderFrameRates(info.layoutLimitedRefreshRate.min,
+ info.layoutLimitedRefreshRate.max);
+ }
+ mVotesStorage.updateVote(
+ displayId, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE, refreshRateVote);
+ mVotesStorage.updateVote(
+ displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, frameRateVote);
}
private void removeUserSettingDisplayPreferredSize(int displayId) {
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 88ee044810db..459f9a6e8f13 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -110,37 +110,40 @@ interface Vote {
int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 13;
// For concurrent displays we want to limit refresh rate on all displays
- int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 14;
+ int PRIORITY_LAYOUT_LIMITED_REFRESH_RATE = 14;
+
+ // For concurrent displays we want to limit refresh rate on all displays
+ int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 15;
// For internal application to limit display modes to specific ids
- int PRIORITY_SYSTEM_REQUESTED_MODES = 15;
+ int PRIORITY_SYSTEM_REQUESTED_MODES = 16;
// PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
// Settings.Global.LOW_POWER_MODE is on.
// Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
// higher priority votes), render rate limit can still apply
- int PRIORITY_LOW_POWER_MODE_MODES = 16;
+ int PRIORITY_LOW_POWER_MODE_MODES = 17;
// PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 17;
+ int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 18;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 18;
+ int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 19;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- int PRIORITY_SKIN_TEMPERATURE = 19;
+ int PRIORITY_SKIN_TEMPERATURE = 20;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- int PRIORITY_PROXIMITY = 20;
+ int PRIORITY_PROXIMITY = 21;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- int PRIORITY_UDFPS = 21;
+ int PRIORITY_UDFPS = 22;
@IntDef(prefix = { "PRIORITY_" }, value = {
PRIORITY_DEFAULT_RENDER_FRAME_RATE,
@@ -157,6 +160,7 @@ interface Vote {
PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE,
PRIORITY_LIMIT_MODE,
PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE,
+ PRIORITY_LAYOUT_LIMITED_REFRESH_RATE,
PRIORITY_LAYOUT_LIMITED_FRAME_RATE,
PRIORITY_SYSTEM_REQUESTED_MODES,
PRIORITY_LOW_POWER_MODE_MODES,
@@ -283,6 +287,8 @@ interface Vote {
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
+ case PRIORITY_LAYOUT_LIMITED_REFRESH_RATE:
+ return "PRIORITY_LAYOUT_LIMITED_REFRESH_RATE";
case PRIORITY_LAYOUT_LIMITED_FRAME_RATE:
return "PRIORITY_LAYOUT_LIMITED_FRAME_RATE";
case PRIORITY_SYSTEM_REQUESTED_MODES:
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ffb2bb6a4528..54bc6fbfd930 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1583,6 +1583,8 @@ public class NotificationManagerService extends SystemService {
// respond to direct replies with updates. So we need to update System UI
// immediately.
if (lifetimeExtensionRefactor()) {
+ // We need to reset this to allow the notif to be updated again.
+ r.setCanceledAfterLifetimeExtension(false);
maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
r.getSbn().getPackageName(), packageImportance);
}
@@ -1639,9 +1641,12 @@ public class NotificationManagerService extends SystemService {
// respond to direct replies with updates. So we need to update System UI
// immediately.
if (lifetimeExtensionRefactor()) {
+ // We need to reset this to allow the notif to be updated again.
+ r.setCanceledAfterLifetimeExtension(false);
maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
r.getSbn().getPackageName(), packageImportance);
}
+
r.recordSmartReplied();
LogMaker logMaker = r.getLogMaker()
.setCategory(MetricsEvent.SMART_REPLY_ACTION)
@@ -11741,17 +11746,37 @@ public class NotificationManagerService extends SystemService {
private void maybeNotifySystemUiListenerLifetimeExtendedLocked(NotificationRecord record,
String pkg, int packageImportance) {
if (record != null && (record.getSbn().getNotification().flags
- & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0
+ && !record.isCanceledAfterLifetimeExtension()) {
boolean isAppForeground = pkg != null && packageImportance == IMPORTANCE_FOREGROUND;
- // Lifetime extended notifications don't need to alert on state change.
+ // Save the original Record's post silently value, so we can restore it after we send
+ // the SystemUI specific silent update.
+ boolean savedPostSilentlyState = record.shouldPostSilently();
+ boolean savedOnlyAlertOnceState = (record.getNotification().flags
+ & FLAG_ONLY_ALERT_ONCE) > 0;
+ // Lifetime extended notifications don't need to alert on new state change.
record.setPostSilently(true);
// We also set FLAG_ONLY_ALERT_ONCE to avoid the notification from HUN-ing again.
record.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ PostNotificationTracker tracker = mPostNotificationTrackerFactory.newTracker(null);
+ tracker.addCleanupRunnable(() -> {
+ synchronized (mNotificationLock) {
+ // Mark that the notification has been updated due to cancelation, so it won't
+ // be updated again if the app cancels multiple times.
+ record.setCanceledAfterLifetimeExtension(true);
+ // Set the post silently status to the record's previous value.
+ record.setPostSilently(savedPostSilentlyState);
+ // Remove FLAG_ONLY_ALERT_ONCE if the notification did not previously have it.
+ if (!savedOnlyAlertOnceState) {
+ record.getNotification().flags &= ~FLAG_ONLY_ALERT_ONCE;
+ }
+ }
+ });
+
mHandler.post(new EnqueueNotificationRunnable(record.getUser().getIdentifier(),
- record, isAppForeground, /* isAppProvided= */ false,
- mPostNotificationTrackerFactory.newTracker(null)));
+ record, isAppForeground, /* isAppProvided= */ false, tracker));
}
}
@@ -13351,17 +13376,23 @@ public class NotificationManagerService extends SystemService {
@ElapsedRealtimeLong private final long mStartTime;
@Nullable private final WakeLock mWakeLock;
private boolean mOngoing;
+ private final List<Runnable> mCleanupRunnables;
@VisibleForTesting
PostNotificationTracker(@Nullable WakeLock wakeLock) {
mStartTime = SystemClock.elapsedRealtime();
mWakeLock = wakeLock;
mOngoing = true;
+ mCleanupRunnables = new ArrayList<Runnable>();
if (DBG) {
Slog.d(TAG, "PostNotification: Started");
}
}
+ void addCleanupRunnable(Runnable runnable) {
+ mCleanupRunnables.add(runnable);
+ }
+
@ElapsedRealtimeLong
long getStartTime() {
return mStartTime;
@@ -13373,8 +13404,9 @@ public class NotificationManagerService extends SystemService {
}
/**
- * Cancels the tracker (releasing the acquired WakeLock). Either {@link #finish} or
- * {@link #cancel} (exclusively) should be called on this object before it's discarded.
+ * Cancels the tracker (releasing the acquired WakeLock) and runs any set cleanup runnables.
+ * Either {@link #finish} or {@link #cancel} (exclusively) should be called on this object
+ * before it's discarded.
*/
void cancel() {
if (!isOngoing()) {
@@ -13385,6 +13417,9 @@ public class NotificationManagerService extends SystemService {
if (mWakeLock != null) {
Binder.withCleanCallingIdentity(() -> mWakeLock.release());
}
+ for (Runnable r : mCleanupRunnables) {
+ r.run();
+ }
if (DBG) {
long elapsedTime = SystemClock.elapsedRealtime() - mStartTime;
Slog.d(TAG, TextUtils.formatSimple("PostNotification: Abandoned after %d ms",
@@ -13393,9 +13428,10 @@ public class NotificationManagerService extends SystemService {
}
/**
- * Finishes the tracker (releasing the acquired WakeLock) and returns the time elapsed since
- * the operation started, in milliseconds. Either {@link #finish} or {@link #cancel}
- * (exclusively) should be called on this object before it's discarded.
+ * Finishes the tracker (releasing the acquired WakeLock), runs any set cleanup runnables,
+ * and returns the time elapsed since the operation started, in milliseconds.
+ * Either {@link #finish} or {@link #cancel} (exclusively) should be called on this object
+ * before it's discarded.
*/
@DurationMillisLong
long finish() {
@@ -13408,6 +13444,9 @@ public class NotificationManagerService extends SystemService {
if (mWakeLock != null) {
Binder.withCleanCallingIdentity(() -> mWakeLock.release());
}
+ for (Runnable r : mCleanupRunnables) {
+ r.run();
+ }
if (DBG) {
Slog.d(TAG,
TextUtils.formatSimple("PostNotification: Finished in %d ms", elapsedTime));
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1392003a13e7..e54124608a26 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -222,6 +222,9 @@ public final class NotificationRecord {
private boolean mPendingLogUpdate = false;
private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
private boolean mSensitiveContent = false;
+ // Whether an app has attempted to cancel this notification after it has been marked as
+ // lifetime extended.
+ private boolean mCanceledAfterLifetimeExtension = false;
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
@@ -535,6 +538,7 @@ public final class NotificationRecord {
+ NotificationListenerService.Ranking.importanceToString(mProposedImportance));
pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
pw.println(prefix + "mSensitiveContent=" + mSensitiveContent);
+ pw.println(prefix + "mCanceledAfterLifetimeExtension=" + mCanceledAfterLifetimeExtension);
pw.println(prefix + "mIntercept=" + mIntercept);
pw.println(prefix + "mHidden==" + mHidden);
pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
@@ -1620,6 +1624,14 @@ public final class NotificationRecord {
mPkgAllowedAsConvo = allowedAsConvo;
}
+ public boolean isCanceledAfterLifetimeExtension() {
+ return mCanceledAfterLifetimeExtension;
+ }
+
+ public void setCanceledAfterLifetimeExtension(boolean canceledAfterLifetimeExtension) {
+ mCanceledAfterLifetimeExtension = canceledAfterLifetimeExtension;
+ }
+
/**
* Whether this notification is a conversation notification.
*/
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 9b868bebd868..5f3c5583e024 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -112,11 +112,16 @@ class TrustedOverlayHost {
final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
for (int i = mOverlays.size() - 1; i >= 0; i--) {
- SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
- if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
- mOverlays.remove(i);
- t.reparent(l.getSurfaceControl(), null);
- l.release();
+ SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+ SurfaceControl overlaySurfaceControl = l.getSurfaceControl();
+ if (overlaySurfaceControl == null) {
+ // Remove the overlay if the surfacepackage was released. Ownership
+ // is shared, so this may happen.
+ mOverlays.remove(i);
+ } else if (overlaySurfaceControl.isSameSurface(p.getSurfaceControl())) {
+ mOverlays.remove(i);
+ t.reparent(l.getSurfaceControl(), null);
+ l.release();
}
}
t.apply();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1290fb7ef91a..a80ee0f66742 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2726,22 +2726,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (Flags.securityLogV2Enabled()) {
- boolean auditLoggingEnabled = Boolean.TRUE.equals(
- mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
- boolean securityLoggingEnabled = Boolean.TRUE.equals(
- mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
- setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
- mInjector.runCryptoSelfTest();
- } else {
- synchronized (getLockObject()) {
- mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
- mInjector.runCryptoSelfTest();
- maybePauseDeviceWideLoggingLocked();
- }
- }
+ boolean auditLoggingEnabled = Boolean.TRUE.equals(
+ mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
+ boolean securityLoggingEnabled = Boolean.TRUE.equals(
+ mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
+ setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+ mInjector.runCryptoSelfTest();
}
/**
@@ -3399,7 +3391,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private void maybeMigrateSecurityLoggingPolicyLocked() {
- if (!Flags.securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
+ if (mOwners.isSecurityLoggingMigrated()) {
return;
}
@@ -16304,9 +16296,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void enforceSecurityLoggingPolicy(boolean enabled) {
- if (!Flags.securityLogV2Enabled()) {
- return;
- }
Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL);
enforceLoggingPolicy(enabled, Boolean.TRUE.equals(auditLoggingEnabled));
@@ -16314,9 +16303,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void enforceAuditLoggingPolicy(boolean enabled) {
- if (!Flags.securityLogV2Enabled()) {
- return;
- }
Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL);
enforceLoggingPolicy(Boolean.TRUE.equals(securityLoggingEnabled), enabled);
@@ -18252,45 +18238,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(who, packageName);
- if (Flags.securityLogV2Enabled()) {
- EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
- who,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- caller.getPackageName(),
- caller.getUserId());
- if (enabled) {
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.SECURITY_LOGGING,
- admin,
- new BooleanPolicyValue(true));
- } else {
- mDevicePolicyEngine.removeGlobalPolicy(
- PolicyDefinition.SECURITY_LOGGING,
- admin);
- }
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
+ if (enabled) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.SECURITY_LOGGING,
+ admin,
+ new BooleanPolicyValue(true));
} else {
- synchronized (getLockObject()) {
- if (who != null) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDefaultDeviceOwner(caller));
- } else {
- // A delegate app passes a null admin component, which is expected
- Preconditions.checkCallAuthorization(
- isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
- }
-
- if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) {
- return;
- }
- mInjector.securityLogSetLoggingEnabledProperty(enabled);
- if (enabled) {
- mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
- maybePauseDeviceWideLoggingLocked();
- } else {
- mSecurityLogMonitor.stop();
- }
- }
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.SECURITY_LOGGING,
+ admin);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SECURITY_LOGGING_ENABLED)
@@ -18312,29 +18273,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mInjector.securityLogGetLoggingEnabledProperty();
}
- if (Flags.securityLogV2Enabled()) {
- final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- caller.getPackageName(),
- caller.getUserId());
- final Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
- PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
- return Boolean.TRUE.equals(policy);
- } else {
- synchronized (getLockObject()) {
- if (admin != null) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDefaultDeviceOwner(caller));
- } else {
- // A delegate app passes a null admin component, which is expected
- Preconditions.checkCallAuthorization(
- isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
- }
- return mInjector.securityLogGetLoggingEnabledProperty();
- }
- }
+ final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ admin,
+ MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
+ final Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
+ return Boolean.TRUE.equals(policy);
}
private void recordSecurityLogRetrievalTime() {
@@ -18410,42 +18356,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- if (Flags.securityLogV2Enabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- admin,
- MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
- caller.getPackageName(),
- caller.getUserId());
-
- synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
- || areAllUsersAffiliatedWithDeviceLocked());
- }
-
- Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
- PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ admin,
+ MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
- if (!Boolean.TRUE.equals(policy)) {
- Slogf.e(LOG_TAG, "%s hasn't enabled security logging but tries to retrieve logs",
- caller.getPackageName());
- return null;
- }
- } else {
- if (admin != null) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isDefaultDeviceOwner(caller));
- } else {
- // A delegate app passes a null admin component, which is expected
- Preconditions.checkCallAuthorization(
- isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
- }
+ synchronized (getLockObject()) {
Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
|| areAllUsersAffiliatedWithDeviceLocked());
+ }
- if (!mInjector.securityLogGetLoggingEnabledProperty()) {
- return null;
- }
+ Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
+
+ if (!Boolean.TRUE.equals(policy)) {
+ Slogf.e(LOG_TAG, "%s hasn't enabled security logging but tries to retrieve logs",
+ caller.getPackageName());
+ return null;
}
recordSecurityLogRetrievalTime();
@@ -18465,10 +18393,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(callingPackage);
- if (!Flags.securityLogV2Enabled()) {
- throw new UnsupportedOperationException("Audit log not enabled");
- }
-
EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
null /* admin */,
MANAGE_DEVICE_POLICY_AUDIT_LOGGING,
@@ -18493,10 +18417,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
- if (!Flags.securityLogV2Enabled()) {
- throw new UnsupportedOperationException("Audit log not enabled");
- }
-
final CallerIdentity caller = getCallerIdentity(callingPackage);
EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
null /* admin */,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 2ea5f168bdd1..52a784559510 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -410,9 +410,8 @@ class OwnersData {
out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);
out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine);
out.attributeBoolean(null, ATTR_MIGRATED_POST_UPGRADE, mPoliciesMigratedPostUpdate);
- if (Flags.securityLogV2Enabled()) {
- out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
- }
+ out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
+
if (Flags.unmanagedModeMigration()) {
out.attributeBoolean(null, ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED,
mRequiredPasswordComplexityMigrated);
@@ -483,8 +482,8 @@ class OwnersData {
null, ATTR_MIGRATED_TO_POLICY_ENGINE, false);
mPoliciesMigratedPostUpdate = parser.getAttributeBoolean(
null, ATTR_MIGRATED_POST_UPGRADE, false);
- mSecurityLoggingMigrated = Flags.securityLogV2Enabled()
- && parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
+ mSecurityLoggingMigrated =
+ parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
mRequiredPasswordComplexityMigrated = Flags.unmanagedModeMigration()
&& parser.getAttributeBoolean(null,
ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED, false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index dd0493032c56..474c48a746c9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -23,7 +23,6 @@ import android.app.admin.DeviceAdminReceiver;
import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.flags.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -184,28 +183,6 @@ class SecurityLogMonitor implements Runnable {
@GuardedBy("mLock")
private final ArrayDeque<SecurityEvent> mAuditLogEventBuffer = new ArrayDeque<>();
- /**
- * Start security logging.
- *
- * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all
- * users on the device.
- */
- void start(int enabledUser) {
- Slog.i(TAG, "Starting security logging for user " + enabledUser);
- mEnabledUser = enabledUser;
- mLock.lock();
- try {
- if (mMonitorThread == null) {
- resetLegacyBufferLocked();
- startMonitorThreadLocked();
- } else {
- Slog.i(TAG, "Security log monitor thread is already running");
- }
- } finally {
- mLock.unlock();
- }
- }
-
void stop() {
Slog.i(TAG, "Stopping security logging.");
mLock.lock();
@@ -467,11 +444,11 @@ class SecurityLogMonitor implements Runnable {
assignLogId(event);
}
- if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+ if (mLegacyLogEnabled) {
addToLegacyBufferLocked(dedupedLogs);
}
- if (Flags.securityLogV2Enabled() && mAuditLogEnabled) {
+ if (mAuditLogEnabled) {
addAuditLogEventsLocked(dedupedLogs);
}
}
@@ -548,7 +525,7 @@ class SecurityLogMonitor implements Runnable {
saveLastEvents(newLogs);
newLogs.clear();
- if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+ if (mLegacyLogEnabled) {
notifyDeviceOwnerOrProfileOwnerIfNeeded(force);
}
} catch (IOException e) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 37e7cfc6ef98..c5c371ff85d5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -106,7 +106,9 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.policy.AttributeCache;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.ProtoLogConfigurationService;
+import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
@@ -256,6 +258,7 @@ import com.android.server.stats.bootstrap.StatsBootstrapAtomService;
import com.android.server.stats.pull.StatsPullAtomService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.supervision.SupervisionService;
import com.android.server.systemcaptions.SystemCaptionsManagerService;
import com.android.server.telecom.TelecomLoaderService;
import com.android.server.testharness.TestHarnessModeService;
@@ -1098,6 +1101,10 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
+ t.traceBegin("InitializeProtoLog");
+ ProtoLog.init(ProtoLogGroup.values());
+ t.traceEnd();
+
// Platform compat service is used by ActivityManagerService, PackageManagerService, and
// possibly others in the future. b/135010838.
t.traceBegin("PlatformCompat");
@@ -1598,6 +1605,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
t.traceEnd();
+ if (android.app.supervision.flags.Flags.supervisionApi()) {
+ t.traceBegin("StartSupervisionService");
+ mSystemServiceManager.startService(SupervisionService.Lifecycle.class);
+ t.traceEnd();
+ }
+
if (!isTv) {
t.traceBegin("StartVibratorManagerService");
mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
diff --git a/services/supervision/Android.bp b/services/supervision/Android.bp
new file mode 100644
index 000000000000..93a0c4af7891
--- /dev/null
+++ b/services/supervision/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.supervision-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.supervision",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.supervision-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
new file mode 100644
index 000000000000..a4ef629492e7
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -0,0 +1,67 @@
+/*
+ * 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.server.supervision;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.supervision.ISupervisionManager;
+import android.content.Context;
+
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/** Service for handling system supervision. */
+public class SupervisionService extends ISupervisionManager.Stub {
+ private static final String LOG_TAG = "SupervisionService";
+
+ private final Context mContext;
+
+ public SupervisionService(Context context) {
+ mContext = context.createAttributionContext("SupervisionService");
+ }
+
+ @Override
+ public boolean isSupervisionEnabled() {
+ return false;
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout, @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
+
+ fout.println("Supervision enabled: " + isSupervisionEnabled());
+ }
+
+ public static class Lifecycle extends SystemService {
+ private final SupervisionService mSupervisionService;
+
+ public Lifecycle(@NonNull Context context) {
+ super(context);
+ mSupervisionService = new SupervisionService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index ab0f0c1fe5ff..d91f154c1b87 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -3556,12 +3556,16 @@ public class DisplayModeDirectorTest {
new RefreshRateRange(refreshRate, refreshRate);
displayListener.onDisplayChanged(DISPLAY_ID);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, refreshRate, refreshRate);
mInjector.mDisplayInfo.layoutLimitedRefreshRate = null;
displayListener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
+ assertNull(vote);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
assertNull(vote);
}
@@ -3585,6 +3589,8 @@ public class DisplayModeDirectorTest {
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
assertNull(vote);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
+ assertNull(vote);
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
index 54f46078d30b..698ce13aa6db 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
@@ -18,7 +18,9 @@ package com.android.server.dreams;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -27,7 +29,9 @@ import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.service.dreams.DreamOverlayService;
+import android.service.dreams.Flags;
import android.service.dreams.IDreamOverlay;
import android.service.dreams.IDreamOverlayCallback;
import android.service.dreams.IDreamOverlayClient;
@@ -221,6 +225,47 @@ public class DreamOverlayServiceTest {
verify(monitor, never()).onWakeUp();
}
+ /**
+ * Verifies that only the currently started dream is able to affect the overlay.
+ */
+ @Test
+ @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT)
+ public void testRedirectToWakeAcrossClients() throws RemoteException {
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mExecutor).execute(any());
+
+ final TestDreamOverlayService.Monitor monitor = Mockito.mock(
+ TestDreamOverlayService.Monitor.class);
+ final TestDreamOverlayService service = new TestDreamOverlayService(monitor, mExecutor);
+ final IBinder binder = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder);
+
+ service.redirectWake(true);
+
+ final IDreamOverlayClient client = getClient(overlay);
+
+ // Start the dream.
+ client.startDream(mLayoutParams, mOverlayCallback,
+ FIRST_DREAM_COMPONENT.flattenToString(), false);
+ // Make sure redirect state is set on dream.
+ verify(mOverlayCallback).onRedirectWake(eq(true));
+
+ // Make sure new changes are propagated.
+ clearInvocations(mOverlayCallback);
+ service.redirectWake(false);
+ verify(mOverlayCallback).onRedirectWake(eq(false));
+
+
+ // Start another dream, make sure new dream is informed of current state.
+ service.redirectWake(true);
+ clearInvocations(mOverlayCallback);
+ client.startDream(mLayoutParams, mOverlayCallback,
+ FIRST_DREAM_COMPONENT.flattenToString(), false);
+ verify(mOverlayCallback).onRedirectWake(eq(true));
+ }
+
private static IDreamOverlayClient getClient(IDreamOverlay overlay) throws RemoteException {
final OverlayClientCallback callback = new OverlayClientCallback();
overlay.getClient(callback);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index f47768280cc1..6ac95c829f56 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -109,6 +109,8 @@ public class FaceEnrollClientTest {
private AidlResponseHandler mAidlResponseHandler;
@Mock
private AuthenticationStateListeners mAuthenticationStateListeners;
+ @Mock
+ private BiometricUtils<Face> mBiometricUtils;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -213,7 +215,7 @@ public class FaceEnrollClientTest {
mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
true /* debugConsent */,
(new FaceEnrollOptions.Builder()).setEnrollReason(ENROLL_SOURCE).build(),
- mAuthenticationStateListeners);
+ mAuthenticationStateListeners, mBiometricUtils);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 6780e60a22b0..bf970867f149 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -50,6 +50,7 @@ import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.UserSwitchProvider;
+import com.android.server.biometrics.sensors.face.FaceUtils;
import org.junit.Before;
import org.junit.Test;
@@ -90,6 +91,8 @@ public class SensorTest {
private AidlSession mCurrentSession;
@Mock
private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ private FaceUtils mBiometricUtils;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -114,7 +117,7 @@ public class SensorTest {
mUserSwitchProvider);
mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mAidlResponseHandlerCallback);
+ mAidlResponseHandlerCallback, mBiometricUtils);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
index 4248e5e37238..24ce569f644e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -206,7 +206,7 @@ public class HidlToAidlSensorAdapterTest {
new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
false /* debugConsent */, (new FaceEnrollOptions.Builder()).build(),
- mAuthenticationStateListeners));
+ mAuthenticationStateListeners, mBiometricUtils));
mLooper.dispatchAll();
verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 698db2e19661..4ef8782386d5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -51,6 +51,7 @@ import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.UserSwitchProvider;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.junit.Before;
@@ -96,6 +97,8 @@ public class SensorTest {
private HandlerThread mThread;
@Mock
AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+ @Mock
+ private FingerprintUtils mBiometricUtils;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -121,7 +124,7 @@ public class SensorTest {
mUserSwitchProvider);
mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mAidlResponseHandlerCallback);
+ mAidlResponseHandlerCallback, mBiometricUtils);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 5a8de58c14ae..0a52238671cd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3047,6 +3047,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void testMultipleCancelOfLifetimeExtendedSendsOneUpdate() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(null);
+ notif.getSbn().getNotification().flags =
+ Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(notif);
+ final StatusBarNotification sbn = notif.getSbn();
+
+ assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+ // Send two cancelations.
+ mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+ mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
+ sbn.getUserId());
+ waitForIdle();
+
+ assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+ // Checks that only one post update is sent.
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.PostNotificationRunnable.class));
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+ FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testCancelAllClearsLifetimeExtended() throws Exception {
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
@@ -6419,12 +6454,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testStats_DirectReplyLifetimeExtendedPostsUpdate() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ // Marks the notification as having already been lifetime extended and canceled.
r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ r.setCanceledAfterLifetimeExtension(true);
+ r.setPostSilently(true);
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
waitForIdle();
+ // At the moment prepareNotifyPostedLocked is called on the listeners,
+ // verify that FLAG_ONLY_ALERT_ONCE and shouldPostSilently are set, regardless of initial
+ // values.
+ doAnswer(
+ invocation -> {
+ int flags = ((NotificationRecord) invocation.getArgument(0))
+ .getSbn().getNotification().flags;
+ assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+ boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+ .shouldPostSilently();
+ assertThat(shouldPostSilently).isTrue();
+ return null;
+ }
+ ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+ // Checks that the record gets marked as a direct reply having occurred.
assertThat(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied())
.isTrue();
// Checks that a post update is sent.
@@ -6437,9 +6491,65 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(captor.getValue().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ // FLAG_ONLY_ALERT_ONCE was not present on the original notification, so it's not here.
assertThat(captor.getValue().getNotification().flags
- & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+ & FLAG_ONLY_ALERT_ONCE).isEqualTo(0);
assertThat(captor.getValue().shouldPostSilently()).isTrue();
+ assertThat(captor.getValue().isCanceledAfterLifetimeExtension()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void testStats_DirectReplyLifetimeExtendedPostsUpdate_RestorePostSilently()
+ throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ // Marks the notification as having already been lifetime extended and canceled.
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ r.setPostSilently(false);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+ waitForIdle();
+
+ // Checks that a post update is sent with shouldPostSilently set to true.
+ doAnswer(
+ invocation -> {
+ boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+ .shouldPostSilently();
+ assertThat(shouldPostSilently).isTrue();
+ return null;
+ }
+ ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+ // Checks that shouldPostSilently is restored to its false state afterward.
+ assertThat(mService.getNotificationRecord(r.getKey()).shouldPostSilently()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+ public void testStats_DirectReplyLifetimeExtendedPostsUpdate_RestoreOnlyAlertOnceFlag()
+ throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ // Marks the notification as having already been lifetime extended and canceled.
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+ waitForIdle();
+
+ // Checks that a post update is sent with FLAG_ONLY_ALERT_ONCE set to true.
+ doAnswer(
+ invocation -> {
+ int flags = ((NotificationRecord) invocation.getArgument(0))
+ .getSbn().getNotification().flags;
+ assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+ return null;
+ }
+ ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+ // Checks that the flag is removed afterward.
+ assertThat(mService.getNotificationRecord(r.getKey()).getSbn().getNotification().flags
+ & FLAG_ONLY_ALERT_ONCE).isEqualTo(0);
}
@Test
@@ -6476,6 +6586,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
anyBoolean());
assertThat(captor.getValue().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+ assertThat(captor.getValue().isCanceledAfterLifetimeExtension()).isFalse();
assertThat(captor.getValue()
.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString())
.isEqualTo("new title");
@@ -9143,11 +9254,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final int replyIndex = 2;
final String reply = "Hello";
final boolean modifiedBeforeSending = true;
- final boolean generatedByAssistant = true;
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
- r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
+ r.getSbn().getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ r.setSuggestionsGeneratedByAssistant(true);
+ r.setCanceledAfterLifetimeExtension(true);
+ r.setPostSilently(true);
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationSmartReplySent(
@@ -9155,6 +9268,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
modifiedBeforeSending);
waitForIdle();
+ // At the moment prepareNotifyPostedLocked is called on the listeners,
+ // verify that FLAG_ONLY_ALERT_ONCE and shouldPostSilently are set, regardless of initial
+ // values.
+ doAnswer(
+ invocation -> {
+ int flags = ((NotificationRecord) invocation.getArgument(0))
+ .getSbn().getNotification().flags;
+ assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+ boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+ .shouldPostSilently();
+ assertThat(shouldPostSilently).isTrue();
+ return null;
+ }
+ ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
// Checks that a post update is sent.
verify(mWorkerHandler, times(1))
.post(any(NotificationManagerService.PostNotificationRunnable.class));
@@ -9165,8 +9293,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(captor.getValue().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ // Flag was present before, so it's set afterward
assertThat(captor.getValue().getNotification().flags
& FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+ // Should post silently was set before, so it's set afterward.
assertThat(captor.getValue().shouldPostSilently()).isTrue();
}