summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt4
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/app/AppOpsManager.java10
-rw-r--r--core/java/android/app/admin/AccountTypePolicyKey.java5
-rw-r--r--core/java/android/app/admin/BundlePolicyValue.java5
-rw-r--r--core/java/android/app/admin/ComponentNamePolicyValue.java5
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java33
-rw-r--r--core/java/android/app/admin/EnforcingAdmin.java4
-rw-r--r--core/java/android/app/admin/LockTaskPolicy.java7
-rw-r--r--core/java/android/app/admin/PackagePermissionPolicyKey.java7
-rw-r--r--core/java/android/app/admin/PackagePolicyKey.java5
-rw-r--r--core/java/android/app/admin/PackageSetPolicyValue.java7
-rw-r--r--core/java/android/app/admin/StringPolicyValue.java5
-rw-r--r--core/java/android/app/admin/UserRestrictionPolicyKey.java5
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig75
-rw-r--r--core/java/android/view/Display.java3
-rw-r--r--core/java/android/view/DisplayInfo.java9
-rw-r--r--core/java/android/view/ImeBackAnimationController.java35
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java9
-rw-r--r--core/java/android/view/InsetsController.java33
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/webkit/flags.aconfig9
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig8
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java43
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_media_AudioSystem.cpp84
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp168
-rw-r--r--core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java54
-rw-r--r--core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java17
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt49
-rw-r--r--media/java/android/media/AudioSystem.java11
-rw-r--r--nfc/api/system-current.txt3
-rw-r--r--nfc/java/android/nfc/INfcOemExtensionCallback.aidl3
-rw-r--r--nfc/java/android/nfc/NfcOemExtension.java216
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt29
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java25
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java33
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt184
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt)11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java10
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java35
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayControl.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java145
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java24
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java2
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java15
-rw-r--r--services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java24
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java90
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java201
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java24
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java61
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java123
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java69
127 files changed, 1982 insertions, 1098 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index ddfd364cc55d..4e6dacff290e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7964,13 +7964,13 @@ package android.app.admin {
field public static final String LOCK_TASK_POLICY = "lockTask";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
- field public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
+ field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
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 public static final String SECURITY_LOGGING_POLICY = "securityLogging";
field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
- field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+ 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/test-current.txt b/core/api/test-current.txt
index a1aa679f01a9..009d08245da2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -597,19 +597,19 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
- method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @Deprecated public int getDeviceOwnerType(@NonNull android.content.ComponentName);
method @Nullable public String getDevicePolicyManagementRoleHolderUpdaterPackage();
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
+ method @FlaggedApi("android.app.admin.flags.headless_device_owner_provisioning_fix_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
method public long getLastBugReportRequestTime();
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
- method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
@@ -680,7 +680,7 @@ package android.app.admin {
}
public final class EnforcingAdmin implements android.os.Parcelable {
- ctor public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
+ ctor @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
}
public final class FlagUnion extends android.app.admin.ResolutionMechanism<java.lang.Integer> {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 21396a1a36e5..8fd332621599 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7459,15 +7459,15 @@ public class AppOpsManager {
}
/**
- * Similar to {@link #onOpChanged(String, String, int)} but includes the device for which
- * the op mode has changed.
+ * Similar to {@link #onOpChanged(String, String)} but includes user and the device for
+ * which the op mode has changed.
*
* <p> Implement this method if callbacks are required on all devices.
* If not implemented explicitly, the default implementation will notify for op changes
- * on the default device {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT} only.
+ * on the default device only.
*
- * <p> If implemented, {@link #onOpChanged(String, String, int)}
- * will not be called automatically.
+ * <p> If implemented, {@link #onOpChanged(String, String)} will not be called
+ * automatically.
*
* @param op The Op that changed.
* @param packageName Package of the app whose Op changed.
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 515c1c66b2a3..02e492bb06aa 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -24,6 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -53,7 +54,9 @@ public final class AccountTypePolicyKey extends PolicyKey {
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
+ }
mAccountType = Objects.requireNonNull((accountType));
}
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index 00e67e64502a..c993671f4fc1 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -30,7 +31,9 @@ public final class BundlePolicyValue extends PolicyValue<Bundle> {
public BundlePolicyValue(Bundle value) {
super(value);
- PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
+ }
}
private BundlePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index f092b7bb5538..a7a2f7d27e0d 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -30,7 +31,9 @@ public final class ComponentNamePolicyValue extends PolicyValue<ComponentName> {
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- PolicySizeVerifier.enforceMaxComponentNameLength(value);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxComponentNameLength(value);
+ }
}
private ComponentNamePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index c0e435c04d3c..156512a90295 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -183,11 +185,13 @@ public final class DevicePolicyIdentifiers {
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
* String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
*/
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0f54cb7bc35e..d31d8f27844a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,8 +54,10 @@ import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_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.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
@@ -10476,6 +10478,10 @@ public class DevicePolicyManager {
@WorkerThread
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
+ if (!Flags.dmrhSetAppRestrictions()) {
+ throwIfParentInstance("setApplicationRestrictions");
+ }
+
if (mService != null) {
try {
mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
@@ -11880,6 +11886,9 @@ public class DevicePolicyManager {
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@Nullable ComponentName admin, String packageName) {
+ if (!Flags.dmrhSetAppRestrictions()) {
+ throwIfParentInstance("getApplicationRestrictions");
+ }
if (mService != null) {
try {
@@ -14224,11 +14233,21 @@ public class DevicePolicyManager {
*/
public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
- UserManager um = mContext.getSystemService(UserManager.class);
- if (!um.isManagedProfile()) {
- throw new SecurityException("The current user does not have a parent profile.");
+ try {
+ if (Flags.dmrhSetAppRestrictions()) {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile()) {
+ throw new SecurityException("The current user does not have a parent profile.");
+ }
+ } else {
+ if (!mService.isManagedProfile(admin)) {
+ throw new SecurityException("The current user does not have a parent profile.");
+ }
+ }
+ return new DevicePolicyManager(mContext, mService, true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return new DevicePolicyManager(mContext, mService, true);
}
/**
@@ -17790,6 +17809,7 @@ public class DevicePolicyManager {
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public void forceSetMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17807,6 +17827,7 @@ public class DevicePolicyManager {
*/
@TestApi
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
if (mService != null) {
try {
@@ -17825,9 +17846,13 @@ public class DevicePolicyManager {
* @hide
*/
@TestApi
+ @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED)
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
@DeviceAdminInfo.HeadlessDeviceOwnerMode
public int getHeadlessDeviceOwnerMode() {
+ if (!Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
+ return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+ }
if (mService != null) {
try {
return mService.getHeadlessDeviceOwnerMode(mContext.getPackageName());
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 5f9bb9c22893..f70a53f61671 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,6 +16,9 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -61,6 +64,7 @@ public final class EnforcingAdmin implements Parcelable {
*
* @hide
*/
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
@TestApi
public EnforcingAdmin(
@NonNull String packageName, @NonNull Authority authority,
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index ab32d46a05ad..68b4ad84d81a 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -19,6 +19,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -134,8 +135,10 @@ public final class LockTaskPolicy extends PolicyValue<LockTaskPolicy> {
}
private void setPackagesInternal(Set<String> packages) {
- for (String p : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(p);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ for (String p : packages) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(p);
+ }
}
mPackages = new HashSet<>(packages);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 226c576d9bc3..1a04f6c908bc 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,8 +59,10 @@ public final class PackagePermissionPolicyKey extends PolicyKey {
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
+ }
mPackageName = Objects.requireNonNull((packageName));
mPermissionName = Objects.requireNonNull((permissionName));
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 8fa21dbb0a2e..9e31a23aec91 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -24,6 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -54,7 +55,9 @@ public final class PackagePolicyKey extends PolicyKey {
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ }
mPackageName = Objects.requireNonNull((packageName));
}
diff --git a/core/java/android/app/admin/PackageSetPolicyValue.java b/core/java/android/app/admin/PackageSetPolicyValue.java
index 24c50b0994d7..8b253a23a299 100644
--- a/core/java/android/app/admin/PackageSetPolicyValue.java
+++ b/core/java/android/app/admin/PackageSetPolicyValue.java
@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -31,8 +32,10 @@ public final class PackageSetPolicyValue extends PolicyValue<Set<String>> {
public PackageSetPolicyValue(@NonNull Set<String> value) {
super(value);
- for (String packageName : value) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ for (String packageName : value) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+ }
}
}
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index bb07c23163ea..6efe9ad0dbed 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -29,7 +30,9 @@ public final class StringPolicyValue extends PolicyValue<String> {
public StringPolicyValue(@NonNull String value) {
super(value);
- PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
+ }
}
private StringPolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index 16cfba4414d5..9054287cb7a0 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -21,6 +21,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -44,7 +45,9 @@ public final class UserRestrictionPolicyKey extends PolicyKey {
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
+ }
mRestriction = Objects.requireNonNull(restriction);
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index e940a7bb96ad..edbbd5b22ddd 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -4,7 +4,6 @@
package: "android.app.admin.flags"
container: "system"
-# Fully rolled out and must not be used.
flag {
name: "policy_engine_migration_v2_enabled"
is_exported: true
@@ -29,6 +28,16 @@ flag {
}
flag {
+ name: "device_policy_size_tracking_internal_bug_fix_enabled"
+ namespace: "enterprise"
+ description: "Bug fix for tracking the total policy size and have a max threshold"
+ bug: "281543351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
namespace: "enterprise"
@@ -68,6 +77,13 @@ flag {
}
flag {
+ name: "permission_migration_for_zero_trust_impl_enabled"
+ namespace: "enterprise"
+ description: "(Implementation) Migrate existing APIs to permission based, and enable DMRH to call them to collect Zero Trust signals."
+ bug: "289520697"
+}
+
+flag {
name: "device_theft_api_enabled"
is_exported: true
namespace: "enterprise"
@@ -210,6 +226,33 @@ flag {
}
flag {
+ name: "headless_device_owner_provisioning_fix_enabled"
+ namespace: "enterprise"
+ description: "Fix provisioning for single-user headless DO"
+ bug: "289515470"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "dmrh_set_app_restrictions"
+ namespace: "enterprise"
+ description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
+ bug: "328758346"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "allow_screen_brightness_control_on_cope"
+ namespace: "enterprise"
+ description: "Allow COPE admin to control screen brightness and timeout."
+ bug: "323894620"
+}
+
+flag {
name: "always_persist_do"
namespace: "enterprise"
description: "Always write device_owners2.xml so that migration flags aren't lost"
@@ -227,6 +270,16 @@ flag {
}
flag {
+ name: "headless_device_owner_delegate_security_logging_bug_fix"
+ namespace: "enterprise"
+ description: "Fix delegate security logging for single user headless DO."
+ bug: "289515470"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "headless_single_user_bad_device_admin_state_fix"
namespace: "enterprise"
description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
@@ -247,6 +300,16 @@ flag {
}
flag {
+ name: "delete_private_space_under_restriction"
+ namespace: "enterprise"
+ description: "Delete private space if user restriction is set"
+ bug: "328758346"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "unmanaged_mode_migration"
namespace: "enterprise"
description: "Migrate APIs for unmanaged mode"
@@ -257,6 +320,16 @@ flag {
}
flag {
+ name: "headless_single_user_fixes"
+ namespace: "enterprise"
+ description: "Various fixes for headless single user mode"
+ bug: "289515470"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "backup_connected_apps_settings"
namespace: "enterprise"
description: "backup and restore connected work and personal apps user settings across devices"
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 82c52a6e8931..b4f2c8b3afd9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1339,7 +1339,7 @@ public final class Display {
public HdrCapabilities getHdrCapabilities() {
synchronized (mLock) {
updateDisplayInfoLocked();
- if (mDisplayInfo.hdrCapabilities == null) {
+ if (mDisplayInfo.hdrCapabilities == null || mDisplayInfo.isForceSdr) {
return null;
}
int[] supportedHdrTypes;
@@ -1361,6 +1361,7 @@ public final class Display {
supportedHdrTypes[index++] = enabledType;
}
}
+
return new HdrCapabilities(supportedHdrTypes,
mDisplayInfo.hdrCapabilities.mMaxLuminance,
mDisplayInfo.hdrCapabilities.mMaxAverageLuminance,
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 157cec8a4d0f..cac3e3c25098 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -230,6 +230,9 @@ public final class DisplayInfo implements Parcelable {
/** The formats disabled by user **/
public int[] userDisabledHdrTypes = {};
+ /** When true, all HDR capabilities are disabled **/
+ public boolean isForceSdr;
+
/**
* Indicates whether the display can be switched into a mode with minimal post
* processing.
@@ -440,6 +443,7 @@ public final class DisplayInfo implements Parcelable {
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
&& Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ && isForceSdr == other.isForceSdr
&& Arrays.equals(userDisabledHdrTypes, other.userDisabledHdrTypes)
&& minimalPostProcessingSupported == other.minimalPostProcessingSupported
&& logicalDensityDpi == other.logicalDensityDpi
@@ -502,6 +506,7 @@ public final class DisplayInfo implements Parcelable {
supportedColorModes = Arrays.copyOf(
other.supportedColorModes, other.supportedColorModes.length);
hdrCapabilities = other.hdrCapabilities;
+ isForceSdr = other.isForceSdr;
userDisabledHdrTypes = other.userDisabledHdrTypes;
minimalPostProcessingSupported = other.minimalPostProcessingSupported;
logicalDensityDpi = other.logicalDensityDpi;
@@ -567,6 +572,7 @@ public final class DisplayInfo implements Parcelable {
supportedColorModes[i] = source.readInt();
}
hdrCapabilities = source.readParcelable(null, android.view.Display.HdrCapabilities.class);
+ isForceSdr = source.readBoolean();
minimalPostProcessingSupported = source.readBoolean();
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
@@ -636,6 +642,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(supportedColorModes[i]);
}
dest.writeParcelable(hdrCapabilities, flags);
+ dest.writeBoolean(isForceSdr);
dest.writeBoolean(minimalPostProcessingSupported);
dest.writeInt(logicalDensityDpi);
dest.writeFloat(physicalXDpi);
@@ -874,6 +881,8 @@ public final class DisplayInfo implements Parcelable {
sb.append(Arrays.toString(appsSupportedModes));
sb.append(", hdrCapabilities ");
sb.append(hdrCapabilities);
+ sb.append(", isForceSdr ");
+ sb.append(isForceSdr);
sb.append(", userDisabledHdrTypes ");
sb.append(Arrays.toString(userDisabledHdrTypes));
sb.append(", minimalPostProcessingSupported ");
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index c7e93c19484f..b80146505a1b 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -149,15 +149,17 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
private void setPreCommitProgress(float progress) {
if (isHideAnimationInProgress()) return;
+ setInterpolatedProgress(BACK_GESTURE.getInterpolation(progress) * PEEK_FRACTION);
+ }
+
+ private void setInterpolatedProgress(float progress) {
if (mWindowInsetsAnimationController != null) {
float hiddenY = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
float shownY = mWindowInsetsAnimationController.getShownStateInsets().bottom;
float imeHeight = shownY - hiddenY;
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int newY = (int) (imeHeight - interpolatedProgress * (imeHeight * PEEK_FRACTION));
+ int newY = (int) (imeHeight - progress * imeHeight);
if (mStartRootScrollY != 0) {
- mViewRoot.setScrollY(
- (int) (mStartRootScrollY * (1 - interpolatedProgress * PEEK_FRACTION)));
+ mViewRoot.setScrollY((int) (mStartRootScrollY * (1 - progress)));
}
mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, newY), 1f,
progress);
@@ -171,21 +173,14 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
return;
}
mTriggerBack = triggerBack;
- int currentBottomInset = mWindowInsetsAnimationController.getCurrentInsets().bottom;
- int targetBottomInset;
- if (triggerBack) {
- targetBottomInset = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
- } else {
- targetBottomInset = mWindowInsetsAnimationController.getShownStateInsets().bottom;
- }
- mPostCommitAnimator = ValueAnimator.ofFloat(currentBottomInset, targetBottomInset);
+ float targetProgress = triggerBack ? 1f : 0f;
+ mPostCommitAnimator = ValueAnimator.ofFloat(
+ BACK_GESTURE.getInterpolation(mLastProgress) * PEEK_FRACTION, targetProgress);
mPostCommitAnimator.setInterpolator(
triggerBack ? STANDARD_ACCELERATE : EMPHASIZED_DECELERATE);
mPostCommitAnimator.addUpdateListener(animation -> {
- int bottomInset = (int) ((float) animation.getAnimatedValue());
if (mWindowInsetsAnimationController != null) {
- mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, bottomInset),
- 1f, animation.getAnimatedFraction());
+ setInterpolatedProgress((float) animation.getAnimatedValue());
} else {
reset();
}
@@ -213,14 +208,8 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
notifyHideIme();
// requesting IME as invisible during post-commit
mInsetsController.setRequestedVisibleTypes(0, ime());
- // Changes the animation state. This also notifies RootView of changed insets, which
- // causes it to reset its scrollY to 0f (animated) if it was panned
mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
- if (mStartRootScrollY != 0 && !triggerBack) {
- // This causes RootView to update its scroll back to the panned position
- mInsetsController.getHost().notifyInsetsChanged();
- }
}
private void notifyHideIme() {
@@ -282,6 +271,10 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
return mPostCommitAnimator != null && mTriggerBack;
}
+ boolean isAnimationInProgress() {
+ return mIsPreCommitAnimationInProgress || mWindowInsetsAnimationController != null;
+ }
+
/**
* Dump information about this ImeBackAnimationController
*
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 6343313b2e01..e90b1c0fc167 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -70,7 +70,14 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
"ImeInsetsSourceConsumer#onAnimationFinished",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
- boolean insetsChanged = super.onAnimationStateChanged(running);
+ boolean insetsChanged = false;
+ if (Flags.predictiveBackIme() && !running && isShowRequested()
+ && mAnimationState == ANIMATION_STATE_HIDE) {
+ // A user controlled hide animation may have ended in the shown state (e.g.
+ // cancelled predictive back animation) -> Insets need to be reset to shown.
+ insetsChanged |= applyLocalVisibilityOverride();
+ }
+ insetsChanged |= super.onAnimationStateChanged(running);
if (running && !isShowRequested()
&& mController.isPredictiveBackImeHideAnimInProgress()) {
// IME predictive back animation switched from pre-commit to post-commit.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7392751cd3a1..8fdf91a2d87c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1197,7 +1197,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
pendingRequest.listener, null /* frame */, true /* fromIme */,
pendingRequest.mInsetsAnimationSpec,
pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation,
- pendingRequest.useInsetsAnimationThread, statsToken);
+ pendingRequest.useInsetsAnimationThread, statsToken,
+ false /* fromPredictiveBack */);
}
@Override
@@ -1333,7 +1334,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, spec,
animationType, getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
- false /* useInsetsAnimationThread */, null);
+ false /* useInsetsAnimationThread */, null, fromPredictiveBack);
}
private void controlAnimationUnchecked(@InsetsType int types,
@@ -1341,7 +1342,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
// Basically, we accept the requested visibilities from the upstream callers...
@@ -1351,7 +1353,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// rejecting showing IME.
controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
- useInsetsAnimationThread, statsToken);
+ useInsetsAnimationThread, statsToken, fromPredictiveBack);
// We are finishing setting the requested visible types. Report them to the server
// and/or the app.
@@ -1363,7 +1365,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
- boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+ boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+ boolean fromPredictiveBack) {
if ((types & mTypesBeingCancelled) != 0) {
final boolean monitoredAnimation =
animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE;
@@ -1449,7 +1452,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
} else {
Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
- fromIme, types, controls, animationType, statsToken);
+ fromIme, types, controls, animationType, statsToken, fromPredictiveBack);
typesReady = typesReadyPair.first;
boolean imeReady = typesReadyPair.second;
if (DEBUG) {
@@ -1585,7 +1588,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types,
SparseArray<InsetsSourceControl> controls, @AnimationType int animationType,
- @Nullable ImeTracker.Token statsToken) {
+ @Nullable ImeTracker.Token statsToken, boolean fromPredictiveBack) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS);
@@ -1597,7 +1600,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
continue;
}
boolean show = animationType == ANIMATION_TYPE_SHOW
- || animationType == ANIMATION_TYPE_USER;
+ || (animationType == ANIMATION_TYPE_USER
+ && (!fromPredictiveBack || !mHost.hasAnimationCallbacks()));
boolean canRun = true;
if (show) {
// Show request
@@ -1620,7 +1624,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
break;
}
} else {
- consumer.requestHide(fromIme, statsToken);
+ consumer.requestHide(fromIme
+ || (fromPredictiveBack && mHost.hasAnimationCallbacks()), statsToken);
}
if (!canRun) {
if (WARN) Log.w(TAG, String.format(
@@ -1675,9 +1680,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@InsetsType int types, boolean fromPredictiveBack) {
- if (fromPredictiveBack) {
- // When insets are animated by predictive back, we want insets to be shown to prevent a
- // jump cut from shown to hidden at the start of the predictive back animation
+ if (fromPredictiveBack && !mHost.hasAnimationCallbacks()) {
+ // When insets are animated by predictive back and the app does not have an animation
+ // callback, we want insets to be shown to prevent a jump cut from shown to hidden at
+ // the start of the predictive back animation
return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
}
// Generally, we want to layout the opposite of the current state. This is to make animation
@@ -2024,7 +2030,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener /* insetsAnimationSpec */,
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
- !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
+ !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken,
+ false /* fromPredictiveBack */);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e81f32e1e64b..523ff38550c1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -141,7 +141,6 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.Vibrator;
import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.text.InputType;
@@ -34156,7 +34155,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Keep in mind that the preferred frame rate affects the frame rate for the next frame,
* so use this method carefully. It's important to note that the preference is valid as
- * long as the View is invalidated.
+ * long as the View is invalidated. Please also be aware that the requested frame rate
+ * will not propagate to child views when this API is used on a ViewGroup.
*
* @param frameRate the preferred frame rate of the view.
*/
@@ -34175,6 +34175,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
* REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
* Note that the frame rate value is valid as long as the View is invalidated.
+ * Please also be aware that the requested frame rate will not propagate to
+ * child views when this API is used on a ViewGroup.
*
* @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
* or value passed to {@link #setRequestedFrameRate(float)}.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0e1625aaedd8..f021bdfe478f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6094,6 +6094,12 @@ public final class ViewRootImpl implements ViewParent,
}
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
+ if (mImeBackAnimationController.isAnimationInProgress()) {
+ // IME predictive back animation is currently in progress which means that scrollY is
+ // currently controlled by ImeBackAnimationController.
+ return false;
+ }
+
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
int scrollY = 0;
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index defe61e506af..b21a490cc506 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -9,3 +9,12 @@ flag {
bug: "319292658"
is_fixed_read_only: true
}
+
+flag {
+ name: "mainline_apis"
+ is_exported: true
+ namespace: "webview"
+ description: "New APIs required by WebViewBootstrap mainline module"
+ bug: "310653407"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 6ce9725f95b0..cd31850b281c 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -71,3 +71,11 @@ flag {
bug: "339720406"
}
+flag {
+ name: "bal_reduce_grace_period"
+ namespace: "responsible_apis"
+ description: "Changes to reduce or ideally remove the grace period exemption."
+ bug: "362575865"
+}
+
+
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b9cc457fecc9..2acda8ad71c1 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -631,21 +631,20 @@ public class ZygoteInit {
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
- long capabilities = posixCapabilitiesAsBits(
- OsConstants.CAP_IPC_LOCK,
- OsConstants.CAP_KILL,
- OsConstants.CAP_NET_ADMIN,
- OsConstants.CAP_NET_BIND_SERVICE,
- OsConstants.CAP_NET_BROADCAST,
- OsConstants.CAP_NET_RAW,
- OsConstants.CAP_SYS_MODULE,
- OsConstants.CAP_SYS_NICE,
- OsConstants.CAP_SYS_PTRACE,
- OsConstants.CAP_SYS_TIME,
- OsConstants.CAP_SYS_TTY_CONFIG,
- OsConstants.CAP_WAKE_ALARM,
- OsConstants.CAP_BLOCK_SUSPEND
- );
+ long capabilities =
+ (1L << OsConstants.CAP_IPC_LOCK) |
+ (1L << OsConstants.CAP_KILL) |
+ (1L << OsConstants.CAP_NET_ADMIN) |
+ (1L << OsConstants.CAP_NET_BIND_SERVICE) |
+ (1L << OsConstants.CAP_NET_BROADCAST) |
+ (1L << OsConstants.CAP_NET_RAW) |
+ (1L << OsConstants.CAP_SYS_MODULE) |
+ (1L << OsConstants.CAP_SYS_NICE) |
+ (1L << OsConstants.CAP_SYS_PTRACE) |
+ (1L << OsConstants.CAP_SYS_TIME) |
+ (1L << OsConstants.CAP_SYS_TTY_CONFIG) |
+ (1L << OsConstants.CAP_WAKE_ALARM) |
+ (1L << OsConstants.CAP_BLOCK_SUSPEND);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
@@ -742,20 +741,6 @@ public class ZygoteInit {
}
/**
- * Gets the bit array representation of the provided list of POSIX capabilities.
- */
- private static long posixCapabilitiesAsBits(int... capabilities) {
- long result = 0;
- for (int capability : capabilities) {
- if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
- throw new IllegalArgumentException(String.valueOf(capability));
- }
- result |= (1L << capability);
- }
- return result;
- }
-
- /**
* This is the entry point for a Zygote process. It creates the Zygote server, loads resources,
* and handles other tasks related to preparing the process for forking into applications.
*
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2abdd57662eb..90cb10aa62b2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -108,6 +108,7 @@ cc_library_shared_for_libandroid_runtime {
"libtracing_perfetto",
"libharfbuzz_ng",
"liblog",
+ "libmediautils",
"libminikin",
"libz",
"server_configurable_flags",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 638591f130ab..46710b5d3edc 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
+#include <atomic>
#define LOG_TAG "AudioSystem-JNI"
#include <android/binder_ibinder_jni.h>
#include <android/binder_libbinder.h>
@@ -34,15 +35,16 @@
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
+#include <mediautils/jthread.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/jni_macros.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include <sys/system_properties.h>
#include <utils/Log.h>
-#include <thread>
#include <optional>
#include <sstream>
#include <memory>
@@ -57,6 +59,7 @@
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
+#include "android_media_JNIUtils.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -3375,42 +3378,53 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE
class JavaSystemPropertyListener {
public:
JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
- mCallback(env->NewGlobalRef(javaCallback)),
- mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}) {
- mListenerThread = std::thread([this]() mutable {
- JNIEnv* threadEnv = GetOrAttachJNIEnvironment(gVm);
- while (!mCleanupSignal.load()) {
- using namespace std::chrono_literals;
- // 1s timeout so this thread can read the cleanup signal to (slowly) be able to
- // be destroyed.
- std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
- if (newVal != "" && mLastVal != newVal) {
- threadEnv->CallVoidMethod(mCallback, gRunnableClassInfo.run);
- mLastVal = std::move(newVal);
+ mCallback {javaCallback, env},
+ mPi {__system_property_find(sysPropName.c_str())},
+ mListenerThread([this](mediautils::stop_token stok) mutable {
+ static const struct timespec close_delay = { .tv_sec = 1 };
+ while (!stok.stop_requested()) {
+ uint32_t old_serial = mSerial.load();
+ uint32_t new_serial;
+ if (__system_property_wait(mPi, old_serial, &new_serial, &close_delay)) {
+ while (new_serial > old_serial) {
+ if (mSerial.compare_exchange_weak(old_serial, new_serial)) {
+ fireUpdate();
+ break;
+ }
+ }
+ }
}
+ }) {}
+
+ void triggerUpdateIfChanged() {
+ uint32_t old_serial = mSerial.load();
+ uint32_t new_serial = __system_property_serial(mPi);
+ while (new_serial > old_serial) {
+ if (mSerial.compare_exchange_weak(old_serial, new_serial)) {
+ fireUpdate();
+ break;
}
- });
+ }
}
- ~JavaSystemPropertyListener() {
- mCleanupSignal.store(true);
- mListenerThread.join();
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm);
- env->DeleteGlobalRef(mCallback);
+ private:
+ void fireUpdate() {
+ const auto threadEnv = GetOrAttachJNIEnvironment(gVm);
+ threadEnv->CallVoidMethod(mCallback.get(), gRunnableClassInfo.run);
}
- private:
- jobject mCallback;
- android::base::CachedProperty mCachedProperty;
- std::thread mListenerThread;
- std::atomic<bool> mCleanupSignal{false};
- std::string mLastVal = "";
+ // Should outlive thread object
+ const GlobalRef mCallback;
+ const prop_info * const mPi;
+ std::atomic<uint32_t> mSerial = 0;
+ const mediautils::jthread mListenerThread;
};
+// A logical set keyed by address
std::vector<std::unique_ptr<JavaSystemPropertyListener>> gSystemPropertyListeners;
std::mutex gSysPropLock{};
-static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
+static jlong android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env, jobject thiz,
jstring sysProp,
jobject javaCallback) {
ScopedUtfChars sysPropChars{env, sysProp};
@@ -3418,6 +3432,19 @@ static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,
std::string{sysPropChars.c_str()});
std::unique_lock _l{gSysPropLock};
gSystemPropertyListeners.push_back(std::move(listener));
+ return reinterpret_cast<jlong>(gSystemPropertyListeners.back().get());
+}
+
+static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env, jobject thiz,
+ jlong nativeHandle) {
+ std::unique_lock _l{gSysPropLock};
+ const auto iter = std::find_if(gSystemPropertyListeners.begin(), gSystemPropertyListeners.end(),
+ [nativeHandle](const auto& x) { return reinterpret_cast<jlong>(x.get()) == nativeHandle; });
+ if (iter != gSystemPropertyListeners.end()) {
+ (*iter)->triggerUpdateIfChanged();
+ } else {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid handle");
+ }
}
@@ -3595,8 +3622,11 @@ static const JNINativeMethod gMethods[] =
MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
- "(Ljava/lang/String;Ljava/lang/Runnable;)V",
+ "(Ljava/lang/String;Ljava/lang/Runnable;)J",
android_media_AudioSystem_listenForSystemPropertyChange),
+ MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
+ "(J)V",
+ android_media_AudioSystem_triggerSystemPropertyUpdate),
};
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index fba0d81d431f..7ad18b83f0d6 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NativeLibraryHelper"
//#define LOG_NDEBUG 0
+#include <android-base/properties.h>
#include <androidfw/ApkParsing.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
@@ -36,6 +37,7 @@
#include <zlib.h>
#include <memory>
+#include <string>
#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -125,72 +127,10 @@ sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char
return INSTALL_SUCCEEDED;
}
-/*
- * Copy the native library if needed.
- *
- * This function assumes the library and path names passed in are considered safe.
- */
-static install_status_t
-copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
-{
- static const size_t kPageSize = getpagesize();
- void** args = reinterpret_cast<void**>(arg);
- jstring* javaNativeLibPath = (jstring*) args[0];
- jboolean extractNativeLibs = *(jboolean*) args[1];
- jboolean debuggable = *(jboolean*) args[2];
-
- ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
-
- uint32_t uncompLen;
- uint32_t when;
- uint32_t crc;
-
- uint16_t method;
- off64_t offset;
- uint16_t extraFieldLength;
- if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
- &extraFieldLength)) {
- ALOGE("Couldn't read zip entry info\n");
- return INSTALL_FAILED_INVALID_APK;
- }
-
- // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
- // easier to use wrap.sh because it only works when it is extracted, see
- // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
- bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
-
- if (!extractNativeLibs && !forceExtractCurrentFile) {
- // check if library is uncompressed and page-aligned
- if (method != ZipFileRO::kCompressStored) {
- ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
- fileName);
- return INSTALL_FAILED_INVALID_APK;
- }
-
- if (offset % kPageSize != 0) {
- ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
- "from apk.\n", fileName, kPageSize);
- return INSTALL_FAILED_INVALID_APK;
- }
-
-#ifdef ENABLE_PUNCH_HOLES
- // if library is uncompressed, punch hole in it in place
- if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
- ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
- "%" PRIu64 "",
- fileName, zipFile->getZipFileName(), offset);
- }
-
- // if extra field for this zip file is present with some length, possibility is that it is
- // padding added for zip alignment. Punch holes there too.
- if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
- ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
- }
-#endif // ENABLE_PUNCH_HOLES
-
- return INSTALL_SUCCEEDED;
- }
-
+static install_status_t extractNativeLibFromApk(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+ const char* fileName,
+ const std::string nativeLibPath, uint32_t when,
+ uint32_t uncompLen, uint32_t crc) {
// Build local file path
const size_t fileNameLen = strlen(fileName);
char localFileName[nativeLibPath.size() + fileNameLen + 2];
@@ -313,6 +253,88 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
}
/*
+ * Copy the native library if needed.
+ *
+ * This function assumes the library and path names passed in are considered safe.
+ */
+static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zipFile,
+ ZipEntryRO zipEntry, const char* fileName) {
+ static const size_t kPageSize = getpagesize();
+ void** args = reinterpret_cast<void**>(arg);
+ jstring* javaNativeLibPath = (jstring*)args[0];
+ jboolean extractNativeLibs = *(jboolean*)args[1];
+ jboolean debuggable = *(jboolean*)args[2];
+ jboolean app_compat_16kb = *(jboolean*)args[3];
+ install_status_t ret = INSTALL_SUCCEEDED;
+
+ ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
+
+ uint32_t uncompLen;
+ uint32_t when;
+ uint32_t crc;
+
+ uint16_t method;
+ off64_t offset;
+ uint16_t extraFieldLength;
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+ &extraFieldLength)) {
+ ALOGE("Couldn't read zip entry info\n");
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
+ // easier to use wrap.sh because it only works when it is extracted, see
+ // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
+ bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
+
+ if (!extractNativeLibs && !forceExtractCurrentFile) {
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (offset % kPageSize != 0) {
+ // If the library is zip-aligned correctly for 4kb devices and app compat is
+ // enabled, on 16kb devices fallback to extraction
+ if (offset % 0x1000 == 0 && app_compat_16kb) {
+ ALOGI("16kB AppCompat: Library '%s' is not PAGE(%zu)-aligned - falling back to "
+ "extraction from apk\n",
+ fileName, kPageSize);
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(),
+ when, uncompLen, crc);
+ }
+
+ ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+ "from apk.\n",
+ fileName, kPageSize);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+#ifdef ENABLE_PUNCH_HOLES
+ // if library is uncompressed, punch hole in it in place
+ if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+ ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+ "%" PRIu64 "",
+ fileName, zipFile->getZipFileName(), offset);
+ }
+
+ // if extra field for this zip file is present with some length, possibility is that it is
+ // padding added for zip alignment. Punch holes there too.
+ if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+ ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+ }
+#endif // ENABLE_PUNCH_HOLES
+
+ return INSTALL_SUCCEEDED;
+ }
+
+ return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(), when,
+ uncompLen, crc);
+}
+
+/*
* An iterator over all shared libraries in a zip file. An entry is
* considered to be a shared library if all of the conditions below are
* satisfied :
@@ -498,12 +520,24 @@ static int findSupportedAbi(JNIEnv* env, jlong apkHandle, jobjectArray supported
return status;
}
+static inline bool app_compat_16kb_enabled() {
+ static const size_t kPageSize = getpagesize();
+
+ // App compat is only applicable on 16kb-page-size devices.
+ if (kPageSize != 0x4000) {
+ return false;
+ }
+
+ return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+}
+
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
jboolean extractNativeLibs, jboolean debuggable)
{
- void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable };
+ jboolean app_compat_16kb = app_compat_16kb_enabled();
+ void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable, &app_compat_16kb };
return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
copyFileIfChanged, reinterpret_cast<void*>(args));
}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 8685326a0173..ecd2f76a5160 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,11 +16,8 @@
package android.os.storage;
-import android.content.res.ObbInfo;
-import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
-import android.os.ServiceManager;
import android.system.ErrnoException;
import androidx.test.filters.LargeTest;
@@ -107,14 +104,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
public void testMountBadPackageNameObb() throws Exception {
final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
String filePath = file.getAbsolutePath();
- try {
- mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
- fail("mountObb should have thrown a exception as package name is incorrect");
- } catch (Exception ex) {
- assertEquals("Path " + filePath
- + " does not contain package name " + mContext.getPackageName(),
- ex.getMessage());
- }
+ mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
}
/**
@@ -164,48 +154,6 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
}
}
- @LargeTest
- public void testObbInfo_withValidObbInfo_success() throws Exception {
- final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
- String filePath = file.getAbsolutePath();
- try {
- mountObb(filePath);
- unmountObb(filePath, DONT_FORCE);
- } catch (Exception ex) {
- fail("No exception expected, got " + ex.getMessage());
- }
- }
-
- @LargeTest
- public void testObbInfo_withInvalidObbInfo_exception() throws Exception {
- final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
- String rawPath = file.getAbsolutePath();
- String canonicalPath = file.getCanonicalPath();
-
- ObbInfo obbInfo = ObbInfo.CREATOR.createFromParcel(Parcel.obtain());
- obbInfo.packageName = "com.android.obbcrash";
- obbInfo.version = 1;
- obbInfo.filename = canonicalPath;
-
- try {
- IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")).mountObb(
- rawPath, canonicalPath, new ObbActionListener(), 0, obbInfo);
- fail("mountObb should have thrown a exception as package name is incorrect");
- } catch (SecurityException ex) {
- assertEquals("Path " + canonicalPath
- + " does not contain package name " + mContext.getPackageName(),
- ex.getMessage());
- }
- }
-
- private static class ObbActionListener extends IObbActionListener.Stub {
- @SuppressWarnings("hiding")
- @Override
- public void onObbResult(String filename, int nonce, int status) {
-
- }
- }
-
private static class MyThreadFactory implements ThreadFactory {
Thread thread = null;
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 4d9b591c0990..00ffda867d6a 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -254,11 +254,8 @@ public class ImeBackAnimationControllerTest {
float progress = 0.5f;
mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// verify correct ime insets manipulation
- float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
- int expectedInset =
- (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, expectedInset)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
}
@Test
@@ -268,12 +265,13 @@ public class ImeBackAnimationControllerTest {
WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
// progress back gesture
- mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+ float progress = 0.5f;
+ mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
// commit back gesture
mBackAnimationController.onBackInvoked();
- // verify setInsetsAndAlpha never called due onReady delayed
+ // verify setInsetsAndAlpha never called due to onReady delayed
verify(mWindowInsetsAnimationController, never()).setInsetsAndAlpha(any(), anyInt(),
anyFloat());
verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
@@ -283,7 +281,7 @@ public class ImeBackAnimationControllerTest {
// verify setInsetsAndAlpha immediately called
verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
- eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+ eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
// verify post-commit hide anim has started
verify(mInsetsController, times(1)).setPredictiveBackImeHideAnimInProgress(eq(true));
});
@@ -319,4 +317,9 @@ public class ImeBackAnimationControllerTest {
return animationControlListener.getValue();
}
+
+ private int getImeHeight(float gestureProgress) {
+ float interpolatedProgress = BACK_GESTURE.getInterpolation(gestureProgress);
+ return (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 22499aeb092c..bec8b1f76394 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -1022,7 +1022,7 @@ public class InsetsControllerTest {
}
@Test
- public void testImeRequestedVisibleDuringPredictiveBackAnim() {
+ public void testImeRequestedVisibleDuringPredictiveBackAnimWithoutCallback() {
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// show ime as initial state
@@ -1051,6 +1051,42 @@ public class InsetsControllerTest {
}
@Test
+ public void testImeRequestedInvisibleDuringPredictiveBackAnimWithCallback() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // set WindowInsetsAnimationCallback on ViewRoot
+ mViewRoot.getView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @Override
+ public WindowInsets onProgress(
+ @NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+ });
+
+ // show ime as initial state
+ mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+ mController.cancelExistingAnimations(); // fast forward show animation
+ assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+ // start control request (for predictive back animation)
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+ listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+ ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+ // Verify that onReady is called (after next predraw)
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ verify(listener).onReady(notNull(), eq(ime()));
+
+ // verify that insets are requested invisible during animation
+ assertFalse(isRequestedVisible(mController, ime()));
+ });
+ }
+
+ @Test
public void testImeShowRequestCancelsPredictiveBackPostCommitAnim() {
prepareControls();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 3e758bbad29b..4622dcffb3cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -349,7 +349,8 @@ public class Bubble implements BubbleViewProvider {
getPackageName(),
getTitle(),
getAppName(),
- isImportantConversation());
+ isImportantConversation(),
+ !isAppLaunchIntent());
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
index 829af08e612a..e873cbd6341d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
@@ -48,10 +48,11 @@ public class BubbleInfo implements Parcelable {
@Nullable
private String mAppName;
private boolean mIsImportantConversation;
+ private boolean mShowAppBadge;
public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName, @Nullable String title, @Nullable String appName,
- boolean isImportantConversation) {
+ boolean isImportantConversation, boolean showAppBadge) {
mKey = key;
mFlags = flags;
mShortcutId = shortcutId;
@@ -61,6 +62,7 @@ public class BubbleInfo implements Parcelable {
mTitle = title;
mAppName = appName;
mIsImportantConversation = isImportantConversation;
+ mShowAppBadge = showAppBadge;
}
private BubbleInfo(Parcel source) {
@@ -73,6 +75,7 @@ public class BubbleInfo implements Parcelable {
mTitle = source.readString();
mAppName = source.readString();
mIsImportantConversation = source.readBoolean();
+ mShowAppBadge = source.readBoolean();
}
public String getKey() {
@@ -115,6 +118,10 @@ public class BubbleInfo implements Parcelable {
return mIsImportantConversation;
}
+ public boolean showAppBadge() {
+ return mShowAppBadge;
+ }
+
/**
* Whether this bubble is currently being hidden from the stack.
*/
@@ -172,6 +179,7 @@ public class BubbleInfo implements Parcelable {
parcel.writeString(mTitle);
parcel.writeString(mAppName);
parcel.writeBoolean(mIsImportantConversation);
+ parcel.writeBoolean(mShowAppBadge);
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 90f8276240a7..4b30ed0dfa7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -734,17 +734,33 @@ class DesktopTasksController(
* Quick-resize to the right or left half of the stable bounds.
*
* @param taskInfo current task that is being snap-resized via dragging or maximize menu button
+ * @param taskSurface the leash of the task being dragged
* @param currentDragBounds current position of the task leash being dragged (or current task
* bounds if being snapped resize via maximize menu button)
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(
taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
currentDragBounds: Rect,
position: SnapPosition
) {
val destinationBounds = getSnapBounds(taskInfo, position)
- if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
+ if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) {
+ // Handle the case where we attempt to snap resize when already snap resized: the task
+ // position won't need to change but we want to animate the surface going back to the
+ // snapped position from the "dragged-to-the-edge" position.
+ if (destinationBounds != currentDragBounds) {
+ returnToDragStartAnimator.start(
+ taskInfo.taskId,
+ taskSurface,
+ startBounds = currentDragBounds,
+ endBounds = destinationBounds,
+ isResizable = taskInfo.isResizeable
+ )
+ }
+ return
+ }
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
@@ -774,13 +790,14 @@ class DesktopTasksController(
taskInfo.taskId,
taskSurface,
startBounds = currentDragBounds,
- endBounds = dragStartBounds
+ endBounds = dragStartBounds,
+ isResizable = taskInfo.isResizeable,
)
} else {
interactionJankMonitor.begin(
taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
)
- snapToHalfScreen(taskInfo, currentDragBounds, position)
+ snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index 4c5258f2bfcd..f4df42cde10f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -48,7 +48,13 @@ class ReturnToDragStartAnimator(
}
/** Builds new animator and starts animation of task leash reposition. */
- fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) {
+ fun start(
+ taskId: Int,
+ taskSurface: SurfaceControl,
+ startBounds: Rect,
+ endBounds: Rect,
+ isResizable: Boolean
+ ) {
val tx = transactionSupplier.get()
boundsAnimator?.cancel()
@@ -81,11 +87,13 @@ class ReturnToDragStartAnimator(
.apply()
taskRepositionAnimationListener.onAnimationEnd(taskId)
boundsAnimator = null
- Toast.makeText(
- context,
- R.string.desktop_mode_non_resizable_snap_text,
- Toast.LENGTH_SHORT
- ).show()
+ if (!isResizable) {
+ Toast.makeText(
+ context,
+ R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
}
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
index 8a9302bcfc98..8ebdc96c21a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
+import android.view.Surface;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -51,8 +52,10 @@ public class PipEnterExitAnimator extends ValueAnimator
@NonNull private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
- private final int mEnterAnimationDuration;
+ private final SurfaceControl.Transaction mFinishTransaction;
+ private final int mEnterExitAnimationDuration;
private final @BOUNDS int mDirection;
+ private final @Surface.Rotation int mRotation;
// optional callbacks for tracking animation start and end
@Nullable private Runnable mAnimationStartCallback;
@@ -62,37 +65,59 @@ public class PipEnterExitAnimator extends ValueAnimator
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
+ @Nullable private final Rect mSourceRectHint;
+ private final Rect mSourceRectHintInsets = new Rect();
+ private final Rect mZeroInsets = new Rect(0, 0, 0, 0);
+
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private final RectEvaluator mRectEvaluator;
+ private final RectEvaluator mInsetEvaluator;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
public PipEnterExitAnimator(Context context,
@NonNull SurfaceControl leash,
SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
@NonNull Rect baseBounds,
@NonNull Rect startBounds,
@NonNull Rect endBounds,
- @BOUNDS int direction) {
+ @Nullable Rect sourceRectHint,
+ @BOUNDS int direction,
+ @Surface.Rotation int rotation) {
mLeash = leash;
mStartTransaction = startTransaction;
+ mFinishTransaction = finishTransaction;
mBaseBounds.set(baseBounds);
mStartBounds.set(startBounds);
mAnimatedRect.set(startBounds);
mEndBounds.set(endBounds);
mRectEvaluator = new RectEvaluator(mAnimatedRect);
+ mInsetEvaluator = new RectEvaluator(new Rect());
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
mDirection = direction;
+ mRotation = rotation;
+
+ mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
+ if (mSourceRectHint != null) {
+ mSourceRectHintInsets.set(
+ mSourceRectHint.left - mBaseBounds.left,
+ mSourceRectHint.top - mBaseBounds.top,
+ mBaseBounds.right - mSourceRectHint.right,
+ mBaseBounds.bottom - mSourceRectHint.bottom
+ );
+ }
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
- mEnterAnimationDuration = context.getResources()
+ mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- setDuration(mEnterAnimationDuration);
+ setObjectValues(startBounds, endBounds);
+ setDuration(mEnterExitAnimationDuration);
setEvaluator(mRectEvaluator);
addListener(this);
addUpdateListener(this);
@@ -118,6 +143,14 @@ public class PipEnterExitAnimator extends ValueAnimator
@Override
public void onAnimationEnd(@NonNull Animator animation) {
+ if (mFinishTransaction != null) {
+ // finishTransaction might override some state (eg. corner radii) so we want to
+ // manually set the state to the end of the animation
+ mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f)
+ .round(mFinishTransaction, mLeash, isInPipDirection())
+ .shadow(mFinishTransaction, mLeash, isInPipDirection());
+ }
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
}
@@ -127,19 +160,32 @@ public class PipEnterExitAnimator extends ValueAnimator
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
+ Rect insets = getInsets(fraction);
+
// TODO (b/350801661): implement fixed rotation
- mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null,
- mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction)
+ mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
+ mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
.round(tx, mLeash, isInPipDirection())
.shadow(tx, mLeash, isInPipDirection());
tx.apply();
}
+ private Rect getInsets(float fraction) {
+ Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
+ Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets;
+
+ return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
+ }
+
private boolean isInPipDirection() {
return mDirection == BOUNDS_ENTER;
}
+ private boolean isOutPipDirection() {
+ return mDirection == BOUNDS_EXIT;
+ }
+
// no-ops
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 7f168800fb29..262c14d2bfe3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -25,6 +25,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
@@ -88,6 +89,11 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
: new PictureInPictureParams.Builder().build());
}
+ @NonNull
+ public PictureInPictureParams getPictureInPictureParams() {
+ return mPictureInPictureParams;
+ }
+
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
PictureInPictureParams params = taskInfo.pictureInPictureParams;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 44baabdd5e2e..f93233ec7461 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -36,6 +36,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -398,17 +399,22 @@ public class PipTransition extends PipTransitionController implements
SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ }
+
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_ENTER);
+ startTransaction, finishTransaction, startBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
this::onClientDrawAtTransitionEnd);
finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
- animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
- });
+ animator.setAnimationEndCallback(() ->
+ finishCallback.onTransitionFinished(finishWct));
animator.start();
return true;
@@ -452,19 +458,53 @@ public class PipTransition extends PipTransitionController implements
TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
if (pipChange == null) {
- return false;
+ // pipChange is null, check to see if we've reparented the PIP activity for
+ // the multi activity case. If so we should use the activity leash instead
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() == null
+ && change.getLastParent() != null
+ && change.getLastParent().equals(pipToken)) {
+ pipChange = change;
+ break;
+ }
+ }
+
+ // failsafe
+ if (pipChange == null) {
+ return false;
+ }
+ }
+
+ // for multi activity, we need to manually set the leash layer
+ if (pipChange.getTaskInfo() == null) {
+ TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
+ if (parent != null) {
+ startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
+ }
}
Rect startBounds = pipChange.getStartAbsBounds();
Rect endBounds = pipChange.getEndAbsBounds();
SurfaceControl pipLeash = pipChange.getLeash();
+ Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+
+ Rect sourceRectHint = null;
+ if (pipChange.getTaskInfo() != null
+ && pipChange.getTaskInfo().pictureInPictureParams != null) {
+ // single activity
+ sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+ } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+ // multi activity
+ sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+ }
PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
- startTransaction, startBounds, startBounds, endBounds,
- PipEnterExitAnimator.BOUNDS_EXIT);
+ startTransaction, finishTransaction, endBounds, startBounds, endBounds,
+ sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);
+
animator.setAnimationEndCallback(() -> {
- finishCallback.onTransitionFinished(null);
mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+ finishCallback.onTransitionFinished(null);
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index ac35459347c6..c88c1e28b011 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -491,7 +491,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} else {
mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
- mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo,
+ mDesktopTasksController.snapToHalfScreen(
+ decoration.mTaskInfo,
+ decoration.mTaskSurface,
decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
left ? SnapPosition.LEFT : SnapPosition.RIGHT);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b85d7936efc2..a9ed13a099f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,10 +16,14 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
+import android.tools.flicker.subject.exceptions.IncorrectRegionException
+import android.tools.flicker.subject.layers.LayerSubject
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +33,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+import kotlin.math.abs
/**
* Test entering pip from an app via auto-enter property when navigating to home.
@@ -67,9 +72,24 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
- @FlakyTest(bugId = 293133362)
+ private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = {
+ val width = visibleRegion.region.bounds.width()
+ val otherWidth = it.visibleRegion.region.bounds.width()
+ if (width < otherWidth && abs(width - otherWidth) > EPSILON) {
+ val errorMsgBuilder =
+ ExceptionMessageBuilder()
+ .forSubject(this)
+ .forIncorrectRegion("width. $width smaller than $otherWidth")
+ .setExpected(width)
+ .setActual(otherWidth)
+ throw IncorrectRegionException(errorMsgBuilder)
+ }
+ }
+
+ @Postsubmit
@Test
override fun pipLayerReduces() {
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
flicker.assertLayers {
val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
@@ -78,6 +98,32 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
}
}
+ /** Checks that [pipApp] window's width is first decreasing then increasing. */
+ @Postsubmit
+ @Test
+ fun pipLayerWidthDecreasesThenIncreases() {
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ flicker.assertLayers {
+ val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+ var previousLayer = pipLayerList[0]
+ var currentLayer = previousLayer
+ var i = 0
+ invoke("layer area is decreasing") {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ previousLayer.widthNotSmallerThan(currentLayer)
+ }
+ }.then().invoke("layer are is increasing", true /* isOptional */) {
+ if (i < pipLayerList.size - 1) {
+ previousLayer = currentLayer
+ currentLayer = pipLayerList[++i]
+ currentLayer.widthNotSmallerThan(previousLayer)
+ }
+ }
+ }
+ }
+
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
@FlakyTest(bugId = 255578530)
@Test
@@ -108,4 +154,9 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
+
+ companion object {
+ // TODO(b/363080056): A margin of error allowed on certain layer size calculations.
+ const val EPSILON = 1
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
index 5b22eddcb6ee..6695a1e56567 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
@@ -41,6 +41,7 @@ class BubbleInfoTest : ShellTestCase() {
"com.some.package",
"title",
"Some app",
+ true,
true
)
val parcel = Parcel.obtain()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 5474e539f286..10557dd9b439 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -123,12 +123,12 @@ import org.mockito.ArgumentMatchers.isA
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.eq
@@ -2859,7 +2859,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun getSnapBounds_calculatesBoundsForResizable() {
+ fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
val bounds = Rect(100, 100, 300, 300)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
topActivityInfo = ActivityInfo().apply {
@@ -2874,13 +2874,45 @@ class DesktopTasksControllerTest : ShellTestCase() {
STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
)
- controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT)
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
}
@Test
+ fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ // Set up task to already be in snapped-left bounds
+ val bounds = Rect(
+ STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+ )
+ val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+ topActivityInfo = ActivityInfo().apply {
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
+ isResizeable = true
+ }
+
+ // Attempt to snap left again
+ val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+ controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
+
+ // Assert that task is NOT updated via WCT
+ verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+
+ // Assert that task leash is updated via Surface Animations
+ verify(mReturnToDragStartAnimator).start(
+ eq(task.taskId),
+ eq(mockSurface),
+ eq(currentDragBounds),
+ eq(bounds),
+ eq(true)
+ )
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
@@ -2911,7 +2943,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
eq(task.taskId),
eq(mockSurface),
eq(currentDragBounds),
- eq(preDragBounds)
+ eq(preDragBounds),
+ eq(false)
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 0b5c6784b73d..be0549b6655d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -113,7 +113,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argThat
@@ -600,6 +600,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
fun testOnDecorSnappedLeft_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -610,8 +611,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
}
@Test
@@ -632,6 +638,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -642,8 +649,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onLeftSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.LEFT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -660,12 +672,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onLeftSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT))
verify(mockToast).show()
}
@Test
fun testOnDecorSnappedRight_snapResizes() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -676,8 +689,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -698,6 +716,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Test
@DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
fun testOnSnapResizeRight_nonResizable_decorSnappedRight() {
+ val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -708,8 +727,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
onRightSnapClickListenerCaptor.value.invoke()
- verify(mockDesktopTasksController)
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ verify(mockDesktopTasksController).snapToHalfScreen(
+ eq(decor.mTaskInfo),
+ taskSurfaceCaptor.capture(),
+ eq(currentBounds),
+ eq(SnapPosition.RIGHT)
+ )
+ assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
}
@Test
@@ -726,7 +750,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onRightSnapClickListenerCaptor.value.invoke()
verify(mockDesktopTasksController, never())
- .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+ .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT))
verify(mockToast).show()
}
@@ -1033,6 +1057,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
+ taskSurface: SurfaceControl = SurfaceControl(),
onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -1051,7 +1076,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>
): DesktopModeWindowDecoration {
val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
- onTaskOpening(decor.mTaskInfo)
+ onTaskOpening(decor.mTaskInfo, taskSurface)
verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a255f730b0f3..ebdfd3e41aa1 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2655,7 +2655,16 @@ public class AudioSystem
/**
* Register a native listener for system property sysprop
* @param callback the listener which fires when the property changes
+ * @return a native handle for use in subsequent methods
* @hide
*/
- public static native void listenForSystemPropertyChange(String sysprop, Runnable callback);
+ public static native long listenForSystemPropertyChange(String sysprop, Runnable callback);
+
+ /**
+ * Trigger a sysprop listener update, if the property has been updated: synchronously validating
+ * there are no pending sysprop changes.
+ * @param handle the handle returned by {@link listenForSystemPropertyChange}
+ * @hide
+ */
+ public static native void triggerSystemPropertyUpdate(long handle);
}
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 717e01e18dbd..0f97b2c8d443 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -73,6 +73,7 @@ package android.nfc {
method public void onApplyRouting(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onBootFinished(int);
method public void onBootStarted();
+ method public void onCardEmulationActivated(boolean);
method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onDisableFinished(int);
method public void onDisableStarted();
@@ -81,6 +82,8 @@ package android.nfc {
method public void onEnableStarted();
method public void onHceEventReceived(int);
method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method public void onRfDiscoveryStarted(boolean);
+ method public void onRfFieldActivated(boolean);
method public void onRoutingChanged();
method public void onStateUpdated(int);
method public void onTagConnected(boolean, @NonNull android.nfc.Tag);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index c19a44ba0ff1..b65c83773618 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -37,4 +37,7 @@ interface INfcOemExtensionCallback {
void onTagDispatch(in ResultReceiver isSkipped);
void onRoutingChanged();
void onHceEventReceived(int action);
+ void onCardEmulationActivated(boolean isActivated);
+ void onRfFieldActivated(boolean isActivated);
+ void onRfDiscoveryStarted(boolean isDiscoveryStarted);
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 6c02edd0eafa..632f693c4fad 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -32,7 +32,9 @@ import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -40,6 +42,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -58,10 +61,13 @@ public final class NfcOemExtension {
private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
private final NfcAdapter mAdapter;
private final NfcOemExtensionCallback mOemNfcExtensionCallback;
+ private boolean mIsRegistered = false;
+ private final Map<Callback, Executor> mCallbackMap = new HashMap<>();
private final Context mContext;
- private Executor mExecutor = null;
- private Callback mCallback = null;
private final Object mLock = new Object();
+ private boolean mCardEmulationActivated = false;
+ private boolean mRfFieldActivated = false;
+ private boolean mRfDiscoveryStarted = false;
/**
* Event that Host Card Emulation is activated.
@@ -215,6 +221,32 @@ public final class NfcOemExtension {
* @param action Flag indicating actions to activate, start and stop cpu boost.
*/
void onHceEventReceived(@HostCardEmulationAction int action);
+
+ /**
+ * Notifies NFC is activated in listen mode.
+ * NFC Forum NCI-2.3 ch.5.2.6 specification
+ *
+ * <p>NFCC is ready to communicate with a Card reader
+ *
+ * @param isActivated true, if card emulation activated, else de-activated.
+ */
+ void onCardEmulationActivated(boolean isActivated);
+
+ /**
+ * Notifies the Remote NFC Endpoint RF Field is activated.
+ * NFC Forum NCI-2.3 ch.5.3 specification
+ *
+ * @param isActivated true, if RF Field is ON, else RF Field is OFF.
+ */
+ void onRfFieldActivated(boolean isActivated);
+
+ /**
+ * Notifies the NFC RF discovery is started or in the IDLE state.
+ * NFC Forum NCI-2.3 ch.5.2 specification
+ *
+ * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle.
+ */
+ void onRfDiscoveryStarted(boolean isDiscoveryStarted);
}
@@ -229,7 +261,12 @@ public final class NfcOemExtension {
/**
* Register an {@link Callback} to listen for NFC oem extension callbacks
+ * Multiple clients can register and callbacks will be invoked asynchronously.
+ *
* <p>The provided callback will be invoked by the given {@link Executor}.
+ * As part of {@link #registerCallback(Executor, Callback)} the
+ * {@link Callback} will be invoked with current NFC state
+ * before the {@link #registerCallback(Executor, Callback)} function completes.
*
* @param executor an {@link Executor} to execute given callback
* @param callback oem implementation of {@link Callback}
@@ -239,15 +276,35 @@ public final class NfcOemExtension {
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull Callback callback) {
synchronized (mLock) {
- if (mCallback != null) {
+ if (executor == null || callback == null) {
+ Log.e(TAG, "Executor and Callback must not be null!");
+ throw new IllegalArgumentException();
+ }
+
+ if (mCallbackMap.containsKey(callback)) {
Log.e(TAG, "Callback already registered. Unregister existing callback before"
+ "registering");
throw new IllegalArgumentException();
}
- NfcAdapter.callService(() -> {
- NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
- mCallback = callback;
- mExecutor = executor;
+ mCallbackMap.put(callback, executor);
+ if (!mIsRegistered) {
+ NfcAdapter.callService(() -> {
+ NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
+ mIsRegistered = true;
+ });
+ } else {
+ updateNfCState(callback, executor);
+ }
+ }
+ }
+
+ private void updateNfCState(Callback callback, Executor executor) {
+ if (callback != null) {
+ Log.i(TAG, "updateNfCState");
+ executor.execute(() -> {
+ callback.onCardEmulationActivated(mCardEmulationActivated);
+ callback.onRfFieldActivated(mRfFieldActivated);
+ callback.onRfDiscoveryStarted(mRfDiscoveryStarted);
});
}
}
@@ -266,15 +323,19 @@ public final class NfcOemExtension {
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public void unregisterCallback(@NonNull Callback callback) {
synchronized (mLock) {
- if (mCallback == null || mCallback != callback) {
+ if (!mCallbackMap.containsKey(callback) || !mIsRegistered) {
Log.e(TAG, "Callback not registered");
throw new IllegalArgumentException();
}
- NfcAdapter.callService(() -> {
- NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
- mCallback = null;
- mExecutor = null;
- });
+ if (mCallbackMap.size() == 1) {
+ NfcAdapter.callService(() -> {
+ NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
+ mIsRegistered = false;
+ mCallbackMap.remove(callback);
+ });
+ } else {
+ mCallbackMap.remove(callback);
+ }
}
}
@@ -322,90 +383,133 @@ public final class NfcOemExtension {
}
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
+
@Override
public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
- synchronized (mLock) {
- if (mCallback == null || mExecutor == null) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mCallback.onTagConnected(connected, tag));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoid2ArgCallback(connected, tag, cb::onTagConnected, ex));
+ }
+
+ @Override
+ public void onCardEmulationActivated(boolean isActivated) throws RemoteException {
+ mCardEmulationActivated = isActivated;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isActivated, cb::onCardEmulationActivated, ex));
+ }
+
+ @Override
+ public void onRfFieldActivated(boolean isActivated) throws RemoteException {
+ mRfFieldActivated = isActivated;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isActivated, cb::onRfFieldActivated, ex));
+ }
+
+ @Override
+ public void onRfDiscoveryStarted(boolean isDiscoveryStarted) throws RemoteException {
+ mRfDiscoveryStarted = isDiscoveryStarted;
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(isDiscoveryStarted, cb::onRfDiscoveryStarted, ex));
}
+
@Override
public void onStateUpdated(int state) throws RemoteException {
- handleVoidCallback(state, mCallback::onStateUpdated);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(state, cb::onStateUpdated, ex));
}
+
@Override
public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onApplyRouting);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onApplyRouting, ex));
}
@Override
public void onNdefRead(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onNdefRead);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onNdefRead, ex));
}
@Override
public void onEnable(ResultReceiver isAllowed) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isAllowed), mCallback::onEnable);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isAllowed), cb::onEnable, ex));
}
@Override
public void onDisable(ResultReceiver isAllowed) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isAllowed), mCallback::onDisable);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isAllowed), cb::onDisable, ex));
}
@Override
public void onBootStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onBootStarted());
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onBootStarted(), ex));
}
@Override
public void onEnableStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onEnableStarted());
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onEnableStarted(), ex));
}
@Override
public void onDisableStarted() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onDisableStarted());
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onDisableStarted(), ex));
}
@Override
public void onBootFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onBootFinished);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onBootFinished, ex));
}
@Override
public void onEnableFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onEnableFinished);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onEnableFinished, ex));
}
@Override
public void onDisableFinished(int status) throws RemoteException {
- handleVoidCallback(status, mCallback::onDisableFinished);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(status, cb::onDisableFinished, ex));
}
@Override
public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException {
- handleVoidCallback(
- new ReceiverWrapper(isSkipped), mCallback::onTagDispatch);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(
+ new ReceiverWrapper(isSkipped), cb::onTagDispatch, ex));
}
@Override
public void onRoutingChanged() throws RemoteException {
- handleVoidCallback(null, (Object input) -> mCallback.onRoutingChanged());
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
}
@Override
public void onHceEventReceived(int action) throws RemoteException {
- handleVoidCallback(action, mCallback::onHceEventReceived);
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(action, cb::onHceEventReceived, ex));
}
- private <T> void handleVoidCallback(T input, Consumer<T> callbackMethod) {
+ private <T> void handleVoidCallback(
+ T input, Consumer<T> callbackMethod, Executor executor) {
synchronized (mLock) {
- if (mCallback == null || mExecutor == null) {
- return;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callbackMethod.accept(input));
+ } catch (RuntimeException ex) {
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ }
+ }
+
+ private <T1, T2> void handleVoid2ArgCallback(
+ T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor) {
+ synchronized (mLock) {
final long identity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> callbackMethod.accept(input));
+ executor.execute(() -> callbackMethod.accept(input1, input2));
+ } catch (RuntimeException ex) {
+ throw ex;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -415,17 +519,12 @@ public final class NfcOemExtension {
private <S, T> S handleNonVoidCallbackWithInput(
S defaultValue, T input, Function<T, S> callbackMethod) throws RemoteException {
synchronized (mLock) {
- if (mCallback == null) {
- return defaultValue;
- }
final long identity = Binder.clearCallingIdentity();
S result = defaultValue;
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
- FutureTask<S> futureTask = new FutureTask<>(
- () -> callbackMethod.apply(input)
- );
- executor.submit(futureTask);
+ FutureTask<S> futureTask = new FutureTask<>(() -> callbackMethod.apply(input));
+ var unused = executor.submit(futureTask);
try {
result = futureTask.get(
OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
@@ -447,17 +546,12 @@ public final class NfcOemExtension {
private <T> T handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod)
throws RemoteException {
synchronized (mLock) {
- if (mCallback == null) {
- return defaultValue;
- }
final long identity = Binder.clearCallingIdentity();
T result = defaultValue;
try {
ExecutorService executor = Executors.newSingleThreadExecutor();
- FutureTask<T> futureTask = new FutureTask<>(
- callbackMethod::get
- );
- executor.submit(futureTask);
+ FutureTask<T> futureTask = new FutureTask<>(callbackMethod::get);
+ var unused = executor.submit(futureTask);
try {
result = futureTask.get(
OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d3949769cd58..157af7d06fed 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -92,6 +92,7 @@
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 97206de346c5..1ce171609e5b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -606,16 +606,6 @@ flag {
}
flag {
- name: "screenshot_private_profile_behavior_fix"
- namespace: "systemui"
- description: "Private profile support for screenshots"
- bug: "327613051"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "screenshot_save_image_exporter"
namespace: "systemui"
description: "Save all screenshots using ImageExporter"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index fe9105ed195e..5f6ea1c0eb43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -131,6 +131,7 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
fakeFeatureFlags,
mSelectedUserInteractor,
keyguardKeyboardInteractor,
+ null,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 0054d137bd2c..2af3b00ed95a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -117,7 +117,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
mEmergencyButtonController, mFalsingCollector, featureFlags,
- mSelectedUserInteractor, keyguardKeyboardInteractor) {
+ mSelectedUserInteractor, keyguardKeyboardInteractor, null) {
@Override
public void onResume(int reason) {
super.onResume(reason);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 281047ad89a0..fabc357c2a68 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -18,8 +18,10 @@
package com.android.keyguard
import android.app.admin.DevicePolicyManager
+import android.app.admin.flags.Flags as DevicePolicyFlags
import android.content.res.Configuration
import android.media.AudioManager
+import android.platform.test.annotations.EnableFlags
import android.telephony.TelephonyManager
import android.testing.TestableLooper.RunWithLooper
import android.testing.TestableResources
@@ -237,6 +239,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
featureFlags,
mSelectedUserInteractor,
keyguardKeyboardInteractor,
+ null,
)
kosmos = testKosmos()
@@ -937,6 +940,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() {
val mainUserId = 10
@@ -953,6 +957,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() {
val secondaryUserId = 10
val mainUserId = 0
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 080b48af2af1..0c5e726e17aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.authentication.domain.interactor
import android.app.admin.DevicePolicyManager
+import android.app.admin.flags.Flags as DevicePolicyFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
@@ -412,6 +414,7 @@ class AuthenticationInteractorTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
fun upcomingWipe() =
testScope.runTest {
val upcomingWipe by collectLastValue(underTest.upcomingWipe)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
index c1615253804c..8c8faee99139 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
@@ -19,9 +19,11 @@ package com.android.systemui.bouncer.ui.viewmodel
import android.content.pm.UserInfo
import android.hardware.biometrics.BiometricFaceConstants
import android.hardware.fingerprint.FingerprintManager
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -35,7 +37,6 @@ import com.android.systemui.biometrics.data.repository.fakeFacePropertyRepositor
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.shared.flag.fakeComposeBouncerFlags
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
@@ -71,6 +72,7 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_COMPOSE_BOUNCER)
class BouncerMessageViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -82,7 +84,6 @@ class BouncerMessageViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
kosmos.fakeUserRepository.setUserInfos(listOf(PRIMARY_USER))
- kosmos.fakeComposeBouncerFlags.composeBouncerEnabled = true
overrideResource(
R.array.config_face_acquire_device_entry_ignorelist,
intArrayOf(ignoreHelpMessageId)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 99fcbb854e3a..777ddab4e259 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -68,15 +68,16 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeManagedProfileController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,6 +123,7 @@ class CommunalInteractorTest : SysuiTestCase() {
private lateinit var userTracker: FakeUserTracker
private lateinit var activityStarter: ActivityStarter
private lateinit var userManager: UserManager
+ private lateinit var managedProfileController: FakeManagedProfileController
private lateinit var underTest: CommunalInteractor
@@ -143,6 +145,7 @@ class CommunalInteractorTest : SysuiTestCase() {
userTracker = kosmos.fakeUserTracker
activityStarter = kosmos.activityStarter
userManager = kosmos.userManager
+ managedProfileController = kosmos.fakeManagedProfileController
whenever(mainUser.isMain).thenReturn(true)
whenever(secondaryUser.isMain).thenReturn(false)
@@ -1070,6 +1073,14 @@ class CommunalInteractorTest : SysuiTestCase() {
}
}
+ @Test
+ fun unpauseWorkProfileEnablesWorkMode() =
+ testScope.runTest {
+ underTest.unpauseWorkProfile()
+
+ assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 3e1f4f6da5e4..3b2b12c4363d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -360,6 +360,7 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase()
}
@Test
+ @DisableSceneContainer
fun alpha_transitionBetweenHubAndDream_isZero() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
@@ -388,8 +389,8 @@ class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase()
ObservableTransitionState.Transition(
fromScene = Scenes.Lockscreen,
toScene = Scenes.Communal,
- emptyFlow(),
- emptyFlow(),
+ flowOf(Scenes.Communal),
+ flowOf(0.5f),
false,
emptyFlow()
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 5fd3a242e195..a18f450cdc9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -209,7 +209,7 @@ class ModesTileDataInteractorTest : SysuiTestCase() {
private companion object {
val TEST_USER = UserHandle.of(1)!!
- val MODES_DRAWABLE_ID = com.android.systemui.res.R.drawable.qs_dnd_icon_off
+ val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
val MODES_DRAWABLE = TestStubDrawable("modes_icon")
val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt
new file mode 100644
index 000000000000..efa13c632087
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.keyguard
+
+import android.os.VibrationAttributes
+import com.google.android.msdl.domain.InteractionProperties
+
+/**
+ * This class represents the set of [InteractionProperties] that only hold [VibrationAttributes] for
+ * the case of user authentication.
+ */
+data class AuthInteractionProperties(
+ override val vibrationAttributes: VibrationAttributes =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
+) : InteractionProperties
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index dad440083f70..64ccbe1a3f5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -19,7 +19,9 @@ package com.android.keyguard;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+import static com.android.systemui.Flags.msdlFeedback;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
@@ -40,6 +42,9 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.HashMap;
import java.util.Map;
@@ -55,6 +60,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
protected boolean mLockedOut;
+ @Nullable
+ protected MSDLPlayer mMSDLPlayer;
private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
// Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
@@ -81,7 +88,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
- FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -89,6 +97,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
+ mMSDLPlayer = msdlPlayer;
}
abstract void resetState();
@@ -178,6 +187,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
+ playAuthenticationHaptics(/* unlock= */true);
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
mDismissing = true;
@@ -185,6 +195,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
}
} else {
+ playAuthenticationHaptics(/* unlock= */false);
mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
if (isValidPassword) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
@@ -201,6 +212,18 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
}
}
+ private void playAuthenticationHaptics(boolean unlock) {
+ if (!msdlFeedback() || mMSDLPlayer == null) return;
+
+ MSDLToken token;
+ if (unlock) {
+ token = MSDLToken.UNLOCK;
+ } else {
+ token = MSDLToken.FAILURE;
+ }
+ mMSDLPlayer.playToken(token, mAuthInteractionProperties);
+ }
+
protected void startErrorAnimation() { /* no-op */ }
protected void verifyPasswordAndUnlock() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index db14a0f67fca..45fdbc6bc888 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -45,6 +45,9 @@ import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.InteractionProperties;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import javax.inject.Inject;
/** Controller for a {@link KeyguardSecurityView}. */
@@ -63,6 +66,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
private final FeatureFlags mFeatureFlags;
protected final SelectedUserInteractor mSelectedUserInteractor;
+ protected final InteractionProperties mAuthInteractionProperties =
+ new AuthInteractionProperties();
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
@@ -214,6 +219,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final SelectedUserInteractor mSelectedUserInteractor;
private final UiEventLogger mUiEventLogger;
private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
+ private final MSDLPlayer mMSDLPlayer;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -228,7 +234,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
KeyguardViewController keyguardViewController,
FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
UiEventLogger uiEventLogger,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ MSDLPlayer msdlPlayer) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -246,6 +253,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mSelectedUserInteractor = selectedUserInteractor;
mUiEventLogger = uiEventLogger;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
+ mMSDLPlayer = msdlPlayer;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -268,14 +276,14 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor);
+ mKeyguardKeyboardInteractor, mMSDLPlayer);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mUiEventLogger, mKeyguardKeyboardInteractor
+ mUiEventLogger, mKeyguardKeyboardInteractor, mMSDLPlayer
);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
@@ -283,14 +291,14 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor);
+ mKeyguardKeyboardInteractor, mMSDLPlayer);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor
+ mKeyguardKeyboardInteractor, mMSDLPlayer
);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 3ad73bc17704..6983a06bb0e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -19,6 +19,7 @@ package com.android.keyguard;
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -55,6 +56,8 @@ import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.List;
public class KeyguardPasswordViewController
@@ -134,10 +137,11 @@ public class KeyguardPasswordViewController
DevicePostureController postureController,
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mPostureController = postureController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 0f61233ac64f..dd7c3e4f8a3a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,9 +16,11 @@
package com.android.keyguard;
+import static com.android.systemui.Flags.msdlFeedback;
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
@@ -40,6 +42,9 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
extends KeyguardAbsKeyInputViewController<T> {
@@ -77,10 +82,11 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
FalsingCollector falsingCollector,
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
@@ -102,12 +108,22 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
return false;
});
button.setAnimationEnabled(showAnimations);
+ button.setMSDLPlayer(mMSDLPlayer);
}
mPasswordEntry.setOnKeyListener(mOnKeyListener);
mPasswordEntry.setUserActivityListener(this::onUserInput);
View deleteButton = mView.findViewById(R.id.delete_button);
- deleteButton.setOnTouchListener(mActionButtonTouchListener);
+ if (msdlFeedback()) {
+ deleteButton.setOnTouchListener((View view, MotionEvent event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_DELETE, null);
+ }
+ return false;
+ });
+ } else {
+ deleteButton.setOnTouchListener(mActionButtonTouchListener);
+ }
deleteButton.setOnClickListener(v -> {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
@@ -119,13 +135,19 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
if (mPasswordEntry.isEnabled()) {
mView.resetPasswordText(true /* animate */, true /* announce */);
}
- mView.doHapticKeyClick();
+ if (msdlFeedback() && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
+ } else {
+ mView.doHapticKeyClick();
+ }
return true;
});
View okButton = mView.findViewById(R.id.key_enter);
if (okButton != null) {
- okButton.setOnTouchListener(mActionButtonTouchListener);
+ if (!msdlFeedback()) {
+ okButton.setOnTouchListener(mActionButtonTouchListener);
+ }
okButton.setOnClickListener(v -> {
if (mPasswordEntry.isEnabled()) {
verifyPasswordAndUnlock();
@@ -177,6 +199,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
for (NumPadKey button : mView.getButtons()) {
button.setOnTouchListener(null);
+ button.setMSDLPlayer(null);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index f4cda0204036..7fc038f98a85 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
+import android.annotation.Nullable;
import android.view.View;
import com.android.internal.logging.UiEvent;
@@ -33,6 +34,8 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -61,11 +64,12 @@ public class KeyguardPinViewController
FalsingCollector falsingCollector,
DevicePostureController postureController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 2d28a189f84d..61f9800c351b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -35,6 +35,7 @@ import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.flags.Flags;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -1139,7 +1140,12 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
int remainingBeforeWipe, int failedAttempts) {
int userType = USER_TYPE_PRIMARY;
if (expiringUserId == userId) {
- int primaryUser = mainUserId != null ? mainUserId : UserHandle.USER_SYSTEM;
+ int primaryUser = UserHandle.USER_SYSTEM;
+ if (Flags.headlessSingleUserFixes()) {
+ if (mainUserId != null) {
+ primaryUser = mainUserId;
+ }
+ }
// TODO: http://b/23522538
if (expiringUserId != primaryUser) {
userType = USER_TYPE_SECONDARY_USER;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 3ef3418bfed4..ce5b5d76332d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -21,6 +21,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
@@ -48,6 +49,8 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
public static final String TAG = "KeyguardSimPinView";
@@ -95,11 +98,12 @@ public class KeyguardSimPinViewController
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 46225c7ea58a..86b29b2aa7d4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -43,6 +44,8 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -92,11 +95,12 @@ public class KeyguardSimPukViewController
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
- KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+ KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+ @Nullable MSDLPlayer msdlPlayer) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor);
+ keyguardKeyboardInteractor, msdlPlayer);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index dcfa775dcabf..4fb80de2d4ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard;
+import static com.android.systemui.Flags.msdlFeedback;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY;
import android.content.Context;
@@ -38,6 +39,9 @@ import androidx.annotation.Nullable;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
/**
* Viewgroup for the bouncer numpad button, specifically for digits.
*/
@@ -57,6 +61,8 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
@Nullable
private NumPadAnimator mAnimator;
private int mOrientation;
+ @Nullable
+ private MSDLPlayer mMSDLPlayer;
private View.OnClickListener mListener = new View.OnClickListener() {
@Override
@@ -221,8 +227,12 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (msdlFeedback() && mMSDLPlayer != null) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_STANDARD, null);
+ } else {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
@Override
@@ -244,4 +254,8 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
super.onInitializeAccessibilityNodeInfo(info);
info.setTextEntryKey(true);
}
+
+ public void setMSDLPlayer(@Nullable MSDLPlayer player) {
+ mMSDLPlayer = player;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 394f8dd629ae..04afd8693e04 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -408,6 +408,10 @@ public class FullscreenMagnificationController implements ComponentCallbacks {
if (!isActivated()) {
return;
}
+ if (!(mFullscreenBorder.getBackground() instanceof GradientDrawable)) {
+ // Wear doesn't use the same magnification border background. So early return here.
+ return;
+ }
float cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
GradientDrawable backgroundDrawable = (GradientDrawable) mFullscreenBorder.getBackground();
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 468737d9372f..732a90d2c01d 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -32,11 +32,11 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.onSubscriberAdded
@@ -254,7 +254,7 @@ constructor(
override val hasLockoutOccurred: StateFlow<Boolean> = _hasLockoutOccurred.asStateFlow()
init {
- if (SceneContainerFlag.isEnabled) {
+ if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
// Hydrate failedAuthenticationAttempts initially and whenever the selected user
// changes.
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 3080e1978b2a..fcba425f0956 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.authentication.domain.interactor
+import android.app.admin.flags.Flags
import android.os.UserHandle
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternView
@@ -288,7 +289,12 @@ constructor(
private suspend fun getWipeTarget(): WipeTarget {
// Check which profile has the strictest policy for failed authentication attempts.
val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe()
- val primaryUser = selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
+ val primaryUser =
+ if (Flags.headlessSingleUserFixes()) {
+ selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
+ } else {
+ UserHandle.USER_SYSTEM
+ }
return when (userToBeWiped) {
selectedUserInteractor.getSelectedUserId() ->
if (userToBeWiped == primaryUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 25d43d972fe2..4c2fe07f92bb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -941,16 +941,16 @@ constructor(
private fun vibrateOnSuccess() {
_hapticsToPlay.value =
HapticsToPlay(
- HapticFeedbackConstants.CONFIRM,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ null,
)
}
private fun vibrateOnError() {
_hapticsToPlay.value =
HapticsToPlay(
- HapticFeedbackConstants.REJECT,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_REJECT,
+ null,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
index 62ef365345b7..a1111f68f1ee 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -17,18 +17,17 @@
package com.android.systemui.bouncer.shared.flag
import com.android.systemui.Flags
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import dagger.Module
-import dagger.Provides
-interface ComposeBouncerFlags {
+object ComposeBouncerFlags {
/**
* Returns `true` if the Compose bouncer is enabled or if the scene container framework is
* enabled; `false` otherwise.
*/
- fun isComposeBouncerOrSceneContainerEnabled(): Boolean
+ fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+ return SceneContainerFlag.isEnabled || Flags.composeBouncer()
+ }
/**
* Returns `true` if only compose bouncer is enabled and scene container framework is not
@@ -39,30 +38,7 @@ interface ComposeBouncerFlags {
"that includes compose bouncer in legacy keyguard.",
replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
)
- fun isOnlyComposeBouncerEnabled(): Boolean
-}
-
-class ComposeBouncerFlagsImpl() : ComposeBouncerFlags {
-
- override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return SceneContainerFlag.isEnabled || Flags.composeBouncer()
- }
-
- @Deprecated(
- "Avoid using this, this is meant to be used only by the glue code " +
- "that includes compose bouncer in legacy keyguard.",
- replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
- )
- override fun isOnlyComposeBouncerEnabled(): Boolean {
+ fun isOnlyComposeBouncerEnabled(): Boolean {
return !SceneContainerFlag.isEnabled && Flags.composeBouncer()
}
}
-
-@Module
-object ComposeBouncerFlagsModule {
- @Provides
- @SysUISingleton
- fun impl(): ComposeBouncerFlags {
- return ComposeBouncerFlagsImpl()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index ad93a25f39a5..cc8dce7938aa 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -55,12 +55,11 @@ constructor(
class BouncerViewBinder
@Inject
constructor(
- private val composeBouncerFlags: ComposeBouncerFlags,
private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
) {
fun bind(view: ViewGroup) {
- if (composeBouncerFlags.isOnlyComposeBouncerEnabled()) {
+ if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
view,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index 102ae7abb3e2..c4bbd9cf0d9f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -8,14 +8,12 @@ import androidx.compose.ui.platform.ComposeView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.compose.theme.PlatformTheme
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.composable.BouncerContent
+import com.android.systemui.bouncer.ui.composable.BouncerContainer
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
-import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.flow.collectLatest
@@ -49,16 +47,7 @@ object ComposeBouncerViewBinder {
this@repeatWhenAttached.lifecycle
}
)
- setContent {
- PlatformTheme {
- BouncerContent(
- rememberViewModel("ComposeBouncerViewBinder") {
- viewModelFactory.create()
- },
- dialogFactory,
- )
- }
- }
+ setContent { BouncerContainer(viewModelFactory, dialogFactory) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
new file mode 100644
index 000000000000..c05dcd5cea83
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.bouncer.ui.composable
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
+
+/** Container that includes the compose bouncer and is meant to be included in legacy keyguard. */
+@Composable
+fun BouncerContainer(
+ viewModelFactory: BouncerSceneContentViewModel.Factory,
+ dialogFactory: BouncerDialogFactory,
+) {
+ PlatformTheme {
+ val backgroundColor = MaterialTheme.colorScheme.surface
+
+ val bouncerViewModel = rememberViewModel("BouncerContainer") { viewModelFactory.create() }
+ Box {
+ Canvas(Modifier.fillMaxSize()) { drawRect(color = backgroundColor) }
+
+ // Separate the bouncer content into a reusable composable that
+ // doesn't have any SceneScope
+ // dependencies
+ BouncerContent(
+ bouncerViewModel,
+ dialogFactory,
+ Modifier.sysuiResTag(Bouncer.TestTags.Root).fillMaxSize()
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index e54dc7dbdebb..c383b8d5f95c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -78,7 +78,6 @@ constructor(
private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
- private val flags: ComposeBouncerFlags,
) : ExclusiveActivatable() {
/**
* A message shown when the user has attempted the wrong credential too many times and now must
@@ -96,7 +95,7 @@ constructor(
val message: MutableStateFlow<MessageViewModel?> = MutableStateFlow(null)
override suspend fun onActivated(): Nothing {
- if (!flags.isComposeBouncerOrSceneContainerEnabled()) {
+ if (!ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
return awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index adc4bc9a14f3..0aada06a7eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -29,7 +29,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
@@ -57,7 +56,6 @@ constructor(
private val authenticationInteractor: AuthenticationInteractor,
private val devicePolicyManager: DevicePolicyManager,
private val bouncerMessageViewModelFactory: BouncerMessageViewModel.Factory,
- private val flags: ComposeBouncerFlags,
private val userSwitcher: UserSwitcherViewModel,
private val actionButtonInteractor: BouncerActionButtonInteractor,
private val pinViewModelFactory: PinBouncerViewModel.Factory,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9b96341bdd8e..b570e14c646a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -61,6 +61,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.ManagedProfileController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.emitOnStart
@@ -116,6 +117,7 @@ constructor(
sceneInteractor: SceneInteractor,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
+ private val managedProfileController: ManagedProfileController
) {
private val logger = Logger(logBuffer, "CommunalInteractor")
@@ -401,12 +403,7 @@ constructor(
/** Request to unpause work profile that is currently in quiet mode. */
fun unpauseWorkProfile() {
- userTracker.userProfiles
- .find { it.isManagedProfile }
- ?.userHandle
- ?.let { userHandle ->
- userManager.requestQuietModeEnabled(/* enableQuietMode */ false, userHandle)
- }
+ managedProfileController.setWorkModeEnabled(true)
}
/** Returns true if work profile is in quiet mode (disabled) for user handle. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8c82900810be..d38c9520eb7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3568,12 +3568,16 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
return;
}
- try {
- mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId());
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to set disable flags: " + flags, e);
+
+ // Handled in StatusBarDisableFlagsInteractor.
+ if (!KeyguardWmStateRefactor.isEnabled()) {
+ try {
+ mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+ mContext.getPackageName(),
+ 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/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index a7a832148130..7899971484f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -369,8 +369,7 @@ object KeyguardRootViewBinder {
} else {
vibratorHelper.performHapticFeedback(
view,
- HapticFeedbackConstants.CONFIRM,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
)
}
}
@@ -390,8 +389,7 @@ object KeyguardRootViewBinder {
} else {
vibratorHelper.performHapticFeedback(
view,
- HapticFeedbackConstants.REJECT,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ HapticFeedbackConstants.BIOMETRIC_REJECT,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index ebdcaa0c91a6..eaa61a113ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -34,20 +34,18 @@ import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
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.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.util.kotlin.BooleanFlowOperators.any
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
@@ -86,6 +84,7 @@ constructor(
private val communalInteractor: CommunalInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+ notificationShadeWindowModel: NotificationShadeWindowModel,
private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
@@ -197,37 +196,18 @@ constructor(
.distinctUntilChanged()
/**
- * Keyguard states which should fully hide the keyguard.
- *
- * Note: [GONE] is not included as it is handled separately.
- */
- private val hiddenKeyguardStates = listOf(OCCLUDED, DREAMING, GLANCEABLE_HUB)
-
- /**
* Keyguard should not show if fully transitioned into a hidden keyguard state or if
* transitioning between hidden states.
*/
private val hideKeyguard: Flow<Boolean> =
- (hiddenKeyguardStates.map { state ->
- keyguardTransitionInteractor
- .transitionValue(state)
- .map { it == 1f }
- .onStart { emit(false) }
- } +
- listOf(
- communalInteractor.isIdleOnCommunal,
- keyguardTransitionInteractor
- .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
- .map { it == 1f }
- .onStart { emit(false) },
- keyguardTransitionInteractor
- .isInTransitionWhere(
- fromStatePredicate = { hiddenKeyguardStates.contains(it) },
- toStatePredicate = { hiddenKeyguardStates.contains(it) },
- )
- .onStart { emit(false) },
- ))
- .any()
+ anyOf(
+ notificationShadeWindowModel.isKeyguardOccluded,
+ communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor
+ .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+ .map { it == 1f }
+ .onStart { emit(false) },
+ )
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 6d63d26d4300..313cb30d84ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -21,6 +21,7 @@ import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.service.quicksettings.Tile
+import androidx.annotation.DrawableRes
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
@@ -98,7 +99,7 @@ constructor(
override fun newTileState(): QSTile.State {
return QSTile.State().apply {
label = mContext.getString(R.string.quick_settings_modes_label)
- icon = ResourceIcon.get(R.drawable.qs_dnd_icon_off)
+ icon = ResourceIcon.get(ICON_RES_ID)
state = Tile.STATE_INACTIVE
}
}
@@ -116,7 +117,7 @@ constructor(
state?.apply {
this.state = tileState.activationState.legacyState
val tileStateIcon = tileState.icon()
- icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(R.drawable.qs_dnd_icon_off)
+ icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID)
label = tileLabel
secondaryLabel = tileState.secondaryLabel
contentDescription = tileState.contentDescription
@@ -127,5 +128,6 @@ constructor(
companion object {
const val TILE_SPEC = "dnd"
+ @DrawableRes val ICON_RES_ID = com.android.internal.R.drawable.ic_zen_priority_modes
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 664951d199a7..6173091b3b99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -55,7 +55,7 @@ constructor(
fun tileData() =
zenModeInteractor.activeModes
.map { activeModes ->
- val modesIconResId = R.drawable.qs_dnd_icon_off
+ val modesIconResId = com.android.internal.R.drawable.ic_zen_priority_modes
if (usesModeIcons()) {
val mainModeDrawable = activeModes.mainMode?.icon?.drawable
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index fe5cbb18f046..000781acec58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -894,11 +894,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
+
+ // Avoid creating TouchInteractionService because the System user in HSUM mode does not
+ // interact with UI elements
+ UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
+ if (UserManager.isHeadlessSystemUserMode() && currentUser.isSystem()) {
+ Log.w(TAG_OPS,
+ "Skipping connection to TouchInteractionService for the System user in HSUM "
+ + "mode.");
+ return;
+ }
try {
mBound = mContext.bindServiceAsUser(mQuickStepIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.of(mUserTracker.getUserId()));
+ currentUser);
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 86b242782308..8fc896c961f7 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -17,7 +17,6 @@
package com.android.systemui.scene
import com.android.systemui.CoreStartable
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.SceneDomainModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
@@ -44,7 +43,6 @@ import dagger.multibindings.IntoMap
[
BouncerSceneModule::class,
CommunalSceneModule::class,
- ComposeBouncerFlagsModule::class,
EmptySceneModule::class,
GoneSceneModule::class,
LockscreenSceneModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index 474afa8bcb9d..56afb79c40d4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -9,7 +9,6 @@ import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.Guideline
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
import com.android.systemui.screenshot.message.ProfileMessageController
@@ -49,44 +48,19 @@ constructor(
}
fun onScreenshotTaken(screenshot: ScreenshotData) {
- if (screenshotPrivateProfileBehaviorFix()) {
- mainScope.launch {
- val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
- var notifiedApps: List<CharSequence> =
- screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
-
- // If profile first run needs to show, bias towards that, otherwise show screenshot
- // detection notification if needed.
- if (profileData != null) {
- workProfileFirstRunView.visibility = View.VISIBLE
- detectionNoticeView.visibility = View.GONE
- profileMessageController.bindView(workProfileFirstRunView, profileData) {
- animateOutMessageContainer()
- }
- animateInMessageContainer()
- } else if (notifiedApps.isNotEmpty()) {
- detectionNoticeView.visibility = View.VISIBLE
- workProfileFirstRunView.visibility = View.GONE
- screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
- animateInMessageContainer()
- }
- }
- } else {
- val workProfileData =
- workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
+ mainScope.launch {
+ val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
var notifiedApps: List<CharSequence> =
screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
- // If work profile first run needs to show, bias towards that, otherwise show screenshot
+ // If profile first run needs to show, bias towards that, otherwise show screenshot
// detection notification if needed.
- if (workProfileData != null) {
+ if (profileData != null) {
workProfileFirstRunView.visibility = View.VISIBLE
detectionNoticeView.visibility = View.GONE
- workProfileMessageController.populateView(
- workProfileFirstRunView,
- workProfileData,
- this::animateOutMessageContainer
- )
+ profileMessageController.bindView(workProfileFirstRunView, profileData) {
+ animateOutMessageContainer()
+ }
animateInMessageContainer()
} else if (notifiedApps.isNotEmpty()) {
detectionNoticeView.visibility = View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
deleted file mode 100644
index 922997d08c25..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot
-
-import android.util.Log
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-
-/** Implementation of [ScreenshotRequestProcessor] */
-class RequestProcessor(
- private val capture: ImageCapture,
- private val policy: ScreenshotPolicy,
-) : ScreenshotRequestProcessor {
-
- override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
- var result = screenshot
-
- // Apply work profile screenshots policy:
- //
- // If the focused app belongs to a work profile, transforms a full screen
- // (or partial) screenshot request to a task snapshot (provided image) screenshot.
-
- // Whenever displayContentInfo is fetched, the topComponent is also populated
- // regardless of the managed profile status.
-
- if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- val info = policy.findPrimaryContent(screenshot.displayId)
- Log.d(TAG, "findPrimaryContent: $info")
- result.taskId = info.taskId
- result.topComponent = info.component
- result.userHandle = info.user
-
- if (policy.isManagedProfile(info.user.identifier)) {
- val image =
- capture.captureTask(info.taskId)
- ?: throw RequestProcessorException("Task snapshot returned a null Bitmap!")
-
- // Provide the task snapshot as the screenshot
- result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
- result.bitmap = image
- result.screenBounds = info.bounds
- }
- }
-
- return result
- }
-}
-
-private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
index 3ad4075a2b89..ee1008d26414 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
@@ -27,5 +27,5 @@ fun interface ScreenshotRequestProcessor {
suspend fun process(original: ScreenshotData): ScreenshotData
}
-/** Exception thrown by [RequestProcessor] if something goes wrong. */
+/** Exception thrown by [ScreenshotRequestProcessor] if something goes wrong. */
class RequestProcessorException(message: String) : IllegalStateException(message)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
index 44f767aa321e..2cb9fe7f1a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
@@ -19,14 +19,11 @@ package com.android.systemui.screenshot.policy
import android.content.ComponentName
import android.content.Context
import android.os.Process
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
import com.android.systemui.SystemUIService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenshot.ImageCapture
-import com.android.systemui.screenshot.RequestProcessor
-import com.android.systemui.screenshot.ScreenshotPolicy
import com.android.systemui.screenshot.ScreenshotRequestProcessor
import com.android.systemui.screenshot.data.repository.DisplayContentRepository
import com.android.systemui.screenshot.data.repository.DisplayContentRepositoryImpl
@@ -68,23 +65,18 @@ interface ScreenshotPolicyModule {
@Application context: Context,
@Background background: CoroutineDispatcher,
imageCapture: ImageCapture,
- policyProvider: Provider<ScreenshotPolicy>,
- displayContentRepoProvider: Provider<DisplayContentRepository>,
+ displayContentRepo: DisplayContentRepository,
policyListProvider: Provider<List<CapturePolicy>>,
): ScreenshotRequestProcessor {
- return if (screenshotPrivateProfileBehaviorFix()) {
- PolicyRequestProcessor(
- background = background,
- capture = imageCapture,
- displayTasks = displayContentRepoProvider.get(),
- policies = policyListProvider.get(),
- defaultOwner = Process.myUserHandle(),
- defaultComponent =
- ComponentName(context.packageName, SystemUIService::class.java.toString())
- )
- } else {
- RequestProcessor(imageCapture, policyProvider.get())
- }
+ return PolicyRequestProcessor(
+ background = background,
+ capture = imageCapture,
+ displayTasks = displayContentRepo,
+ policies = policyListProvider.get(),
+ defaultOwner = Process.myUserHandle(),
+ defaultComponent =
+ ComponentName(context.packageName, SystemUIService::class.java.toString())
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 56ea00cf0954..7ef1e416aa19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -22,6 +22,7 @@ import android.os.UserManager;
import androidx.annotation.NonNull;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
@@ -43,17 +44,20 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
private final UserManager mUserManager;
private final UserTracker mUserTracker;
private final LinkedList<UserInfo> mProfiles;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private boolean mListening;
private int mCurrentUser;
@Inject
public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
- UserTracker userTracker, UserManager userManager) {
+ UserTracker userTracker, UserManager userManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mMainExecutor = mainExecutor;
mUserManager = userManager;
mUserTracker = userTracker;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mProfiles = new LinkedList<>();
}
@@ -80,6 +84,7 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
StatusBarManager statusBarManager = (StatusBarManager) mContext
.getSystemService(android.app.Service.STATUS_BAR_SERVICE);
statusBarManager.collapsePanels();
+ mKeyguardUpdateMonitor.awakenFromDream();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 21ec14fc7f03..591d7af44db1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -403,7 +403,7 @@ interface PolicyModule {
tileSpec = TileSpec.create(DND_TILE_SPEC),
uiConfig =
QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_dnd_icon_off,
+ iconRes = com.android.internal.R.drawable.ic_zen_priority_modes,
labelRes = R.string.quick_settings_modes_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index e724c60bfc54..487432eb5886 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,6 +29,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
@@ -43,9 +46,13 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.res.R;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.google.android.msdl.data.model.MSDLToken;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -86,6 +93,8 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+ private KosmosJavaAdapter mKosmosJavaAdapter = new KosmosJavaAdapter(this);
+ private final FakeMSDLPlayer mMSDLPlayer = mKosmosJavaAdapter.getMsdlPlayer();
@Before
public void setup() {
@@ -108,7 +117,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
- mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor) {
+ mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor, mMSDLPlayer) {
@Override
void resetState() {
}
@@ -197,4 +206,32 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true);
verify(mAbsKeyInputView, never()).setPasswordEntryEnabled(true);
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withMSDLFeedback_withMatch_playsUnlockToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.UNLOCK);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withoutMSDLFeedback_withMatch_doesNotPlayToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withMSDLFeedback_withoutMatch_playsFailureToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.FAILURE);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ public void onPasswordChecked_withoutMSDLFeedback_withoutMatch_doesNotPlayToken() {
+ mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+ assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 36d4d122ab11..c43a1849d2a7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -149,7 +149,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
featureFlags,
mSelectedUserInteractor,
uiEventLogger,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 7151c429acf9..ea6c1bc5b31c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -101,7 +101,8 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
)
underTest.init()
underTest.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index acae913459b3..c26365d00376 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -96,7 +96,8 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() {
emergencyButtonController,
fakeFeatureFlags,
mSelectedUserInteractor,
- keyguardKeyboardInteractor
+ keyguardKeyboardInteractor,
+ null,
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 6047e7d1bf79..4fc41669b2c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -717,13 +717,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(confirmHaptics?.hapticFeedbackConstant)
.isEqualTo(
if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
- else HapticFeedbackConstants.CONFIRM
- )
- assertThat(confirmHaptics?.flag)
- .isEqualTo(
- if (expectConfirmation) null
- else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
+ else HapticFeedbackConstants.BIOMETRIC_CONFIRM
)
+ assertThat(confirmHaptics?.flag).isNull()
if (expectConfirmation) {
kosmos.promptViewModel.confirmAuthenticated()
@@ -731,9 +727,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(confirmedHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(confirmedHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(confirmedHaptics?.flag).isNull()
}
@Test
@@ -747,9 +742,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(currentHaptics?.flag).isNull()
}
@Test
@@ -757,9 +751,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ assertThat(currentHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(currentHaptics?.flag).isNull()
}
// biometricprompt_sfps_fingerprint_authenticating reused across rotations
@@ -870,8 +864,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
)
val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ assertThat(haptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics?.flag).isNull()
}
@Test
@@ -901,10 +896,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
if (expectConfirmation) {
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ assertThat(haptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics?.flag).isNull()
} else {
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(haptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index 8435b1cb71dc..d2dcf4d38d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles
-import android.graphics.drawable.TestStubDrawable
import android.os.Handler
import android.platform.test.annotations.EnableFlags
import android.service.quicksettings.Tile
@@ -93,12 +92,7 @@ class ModesTileTest : SysuiTestCase() {
ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
private val mapper =
ModesTileMapper(
- context.orCreateTestableResources
- .apply {
- addOverride(R.drawable.qs_dnd_icon_on, TestStubDrawable())
- addOverride(R.drawable.qs_dnd_icon_off, TestStubDrawable())
- }
- .resources,
+ context.resources,
context.theme,
)
@@ -122,7 +116,7 @@ class ModesTileTest : SysuiTestCase() {
QSTileConfigTestBuilder.build {
uiConfig =
QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_dnd_icon_off,
+ iconRes = ModesTile.ICON_RES_ID,
labelRes = R.string.quick_settings_modes_label,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index b31d21e7f8b2..15da77dfe33d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -2,8 +2,6 @@ package com.android.systemui.screenshot
import android.graphics.drawable.Drawable
import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -90,20 +88,6 @@ class MessageContainerControllerTest : SysuiTestCase() {
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
- fun testOnScreenshotTakenUserHandle_withWorkProfileFirstRun() {
- whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle)))
- .thenReturn(workProfileData)
- messageContainer.onScreenshotTaken(screenshotData)
-
- verify(workProfileMessageController)
- .populateView(eq(workProfileFirstRunView), eq(workProfileData), any())
- assertEquals(View.VISIBLE, workProfileFirstRunView.visibility)
- assertEquals(View.GONE, detectionNoticeView.visibility)
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
fun testOnScreenshotTakenUserHandle_withProfileProfileFirstRun() = runTest {
val profileData =
ProfileMessageController.ProfileFirstRunData(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
deleted file mode 100644
index 0847f01f12d3..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot
-
-import android.content.ComponentName
-import android.graphics.Bitmap
-import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
-import android.hardware.HardwareBuffer
-import android.os.UserHandle
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
-import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert
-import org.junit.Test
-
-private const val USER_ID = 1
-private const val TASK_ID = 1
-
-class RequestProcessorTest {
- private val imageCapture = FakeImageCapture()
- private val component = ComponentName("android.test", "android.test.Component")
- private val bounds = Rect(25, 25, 75, 75)
-
- private val policy = FakeScreenshotPolicy()
-
- @Test
- fun testFullScreenshot() = runBlocking {
- // Indicate that the primary content belongs to a normal user
- policy.setManagedProfile(USER_ID, false)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- val processedData = processor.process(ScreenshotData.fromRequest(request))
-
- // Request has topComponent added, but otherwise unchanged.
- assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
- assertThat(processedData.source).isEqualTo(SCREENSHOT_OTHER)
- assertThat(processedData.topComponent).isEqualTo(component)
- }
-
- @Test
- fun testFullScreenshot_managedProfile() = runBlocking {
- // Provide a fake task bitmap when asked
- val bitmap = makeHardwareBitmap(100, 100)
- imageCapture.image = bitmap
-
- // Indicate that the primary content belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- val processedData = processor.process(ScreenshotData.fromRequest(request))
-
- // Expect a task snapshot is taken, overriding the full screen mode
- assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
- assertThat(processedData.bitmap).isEqualTo(bitmap)
- assertThat(processedData.screenBounds).isEqualTo(bounds)
- assertThat(processedData.insets).isEqualTo(Insets.NONE)
- assertThat(processedData.taskId).isEqualTo(TASK_ID)
- assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
- }
-
- @Test
- fun testFullScreenshot_managedProfile_nullBitmap() {
- // Provide a null task bitmap when asked
- imageCapture.image = null
-
- // Indicate that the primary content belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
- policy.setDisplayContentInfo(
- policy.getDefaultDisplayId(),
- DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
- )
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
- val processor = RequestProcessor(imageCapture, policy)
-
- Assert.assertThrows(IllegalStateException::class.java) {
- runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
- }
- }
-
- @Test
- fun testProvidedImageScreenshot() = runBlocking {
- val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy)
-
- policy.setManagedProfile(USER_ID, false)
-
- val bitmap = makeHardwareBitmap(100, 100)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
- .setTopComponent(component)
- .setTaskId(TASK_ID)
- .setUserId(USER_ID)
- .setBitmap(bitmap)
- .setBoundsOnScreen(bounds)
- .setInsets(Insets.NONE)
- .build()
-
- val screenshotData = ScreenshotData.fromRequest(request)
- val processedData = processor.process(screenshotData)
-
- assertThat(processedData).isEqualTo(screenshotData)
- }
-
- @Test
- fun testProvidedImageScreenshot_managedProfile() = runBlocking {
- val bounds = Rect(50, 50, 150, 150)
- val processor = RequestProcessor(imageCapture, policy)
-
- // Indicate that the screenshot belongs to a manged profile
- policy.setManagedProfile(USER_ID, true)
-
- val bitmap = makeHardwareBitmap(100, 100)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
- .setTopComponent(component)
- .setTaskId(TASK_ID)
- .setUserId(USER_ID)
- .setBitmap(bitmap)
- .setBoundsOnScreen(bounds)
- .setInsets(Insets.NONE)
- .build()
-
- val screenshotData = ScreenshotData.fromRequest(request)
- val processedData = processor.process(screenshotData)
-
- // Work profile, but already a task snapshot, so no changes
- assertThat(processedData).isEqualTo(screenshotData)
- }
-
- private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
- val buffer =
- HardwareBuffer.create(
- width,
- height,
- HardwareBuffer.RGBA_8888,
- 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
- )
- return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
- }
-
- private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
- return bitmap.hardwareBuffer == this.hardwareBuffer && bitmap.colorSpace == this.colorSpace
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
index 2f810276d7b2..c7919df7e0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
@@ -20,6 +20,7 @@ import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
@@ -35,6 +36,7 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.never
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -46,12 +48,20 @@ class ManagedProfileControllerImplTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- controller = ManagedProfileControllerImpl(context, mainExecutor, userTracker, userManager)
+ controller =
+ ManagedProfileControllerImpl(
+ context,
+ mainExecutor,
+ userTracker,
+ userManager,
+ keyguardUpdateMonitor
+ )
}
@Test
@@ -107,6 +117,24 @@ class ManagedProfileControllerImplTest : SysuiTestCase() {
captor.value.onProfilesChanged(userManager.getEnabledProfiles(1))
}
+ @Test
+ fun hasWorkingProfile_setWorkModeEnabled_callsAwakenFromDream() {
+ `when`(userTracker.userId).thenReturn(1)
+ setupWorkingProfile(1)
+ `when`(userManager.requestQuietModeEnabled(any(), any())).thenReturn(false)
+ controller.hasActiveProfile()
+
+ controller.isWorkModeEnabled = true
+
+ verify(keyguardUpdateMonitor).awakenFromDream()
+ }
+
+ @Test
+ fun noWorkingProfile_setWorkModeEnabled_NoAwakenFromDreamCall() {
+ controller.isWorkModeEnabled = true
+ verify(keyguardUpdateMonitor, never()).awakenFromDream()
+ }
+
private fun setupWorkingProfile(userId: Int) {
`when`(userManager.getEnabledProfiles(userId))
.thenReturn(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
deleted file mode 100644
index 7482c0feb56a..000000000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.bouncer.shared.flag
-
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-
-class FakeComposeBouncerFlags(var composeBouncerEnabled: Boolean = false) : ComposeBouncerFlags {
- override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
- return SceneContainerFlag.isEnabled || composeBouncerEnabled
- }
-
- @Deprecated(
- "Avoid using this, this is meant to be used only by the glue code " +
- "that includes compose bouncer in legacy keyguard.",
- replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
- )
- override fun isOnlyComposeBouncerEnabled(): Boolean = composeBouncerEnabled
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index e8612d084b14..5c5969d359c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -22,7 +22,6 @@ import android.content.applicationContext
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.deviceentry.domain.interactor.biometricMessageInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
@@ -45,7 +44,6 @@ val Kosmos.bouncerMessageViewModel by Fixture {
faceAuthInteractor = deviceEntryFaceAuthInteractor,
deviceUnlockedInteractor = deviceUnlockedInteractor,
deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
- flags = composeBouncerFlags,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index e405d17166b9..171be97bf964 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -25,7 +25,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -55,7 +54,6 @@ val Kosmos.bouncerSceneContentViewModel by Fixture {
authenticationInteractor = authenticationInteractor,
devicePolicyManager = devicePolicyManager,
bouncerMessageViewModelFactory = bouncerMessageViewModelFactory,
- flags = composeBouncerFlags,
userSwitcher = userSwitcherViewModel,
actionButtonInteractor = bouncerActionButtonInteractor,
pinViewModelFactory = pinBouncerViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 4ad046cc095e..629fda6b610c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -36,6 +36,7 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.activityStarter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -61,6 +62,7 @@ val Kosmos.communalInteractor by Fixture {
sceneInteractor = sceneInteractor,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
+ managedProfileController = fakeManagedProfileController
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index b9443bcaf650..7cf4083e8407 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -25,6 +25,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -39,6 +40,7 @@ val Kosmos.keyguardRootViewModel by Fixture {
communalInteractor = communalInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+ notificationShadeWindowModel = notificationShadeWindowModel,
alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
alternateBouncerToLockscreenTransitionViewModel =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 953363d010fe..851a378f3165 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -36,6 +36,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -154,4 +155,5 @@ class KosmosJavaAdapter() {
val scrimController by lazy { kosmos.scrimController }
val scrimStartable by lazy { kosmos.scrimStartable }
val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
+ val msdlPlayer by lazy { kosmos.msdlPlayer }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
index 60d97d1b1437..ef04b9d907f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
@@ -14,9 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.bouncer.shared.flag
+@file:OptIn(ExperimentalCoroutinesApi::class)
+package com.android.systemui.statusbar.phone
+
+import android.testing.LeakCheck
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.utils.leaks.FakeManagedProfileController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
+val Kosmos.fakeManagedProfileController by Fixture { FakeManagedProfileController(LeakCheck()) }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
index 845249e2c82f..ab94e989f8b3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
@@ -39,8 +39,14 @@ public final class MouseEventHandler {
* @param displayId The display that is being magnified
*/
public void onEvent(MotionEvent event, int displayId) {
- if (event.getAction() == ACTION_HOVER_MOVE
- || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE)) {
+ // Ignore gesture events synthesized from the touchpad.
+ // TODO(b/354696546): Use synthesized pinch gestures to control scale.
+ boolean isSynthesizedFromTouchpad =
+ event.getClassification() != MotionEvent.CLASSIFICATION_NONE;
+
+ // Consume only move events from the mouse or hovers from any tool.
+ if (!isSynthesizedFromTouchpad && (event.getAction() == ACTION_HOVER_MOVE
+ || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE))) {
final float eventX = event.getX();
final float eventY = event.getY();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d86bae19f174..07e5f2e34ab8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,9 +224,6 @@ class StorageManagerService extends IStorageManager.Stub
/** Extended timeout for the system server watchdog for vold#partition operation. */
private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
- private static final Pattern OBB_FILE_PATH = Pattern.compile(
- "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/obb/)([^/]+)/([^/]+\\.obb)");
-
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -3147,9 +3144,7 @@ class StorageManagerService extends IStorageManager.Stub
Objects.requireNonNull(rawPath, "rawPath cannot be null");
Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
Objects.requireNonNull(token, "token cannot be null");
- Objects.requireNonNull(obbInfo, "obbInfo cannot be null");
-
- validateObbInfo(obbInfo, rawPath);
+ Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
final int callingUid = Binder.getCallingUid();
final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -3161,34 +3156,6 @@ class StorageManagerService extends IStorageManager.Stub
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- private void validateObbInfo(ObbInfo obbInfo, String rawPath) {
- String obbFilePath;
- try {
- obbFilePath = new File(rawPath).getCanonicalPath();
- } catch (IOException ex) {
- throw new RuntimeException("Failed to resolve path" + rawPath + " : " + ex);
- }
-
- Matcher matcher = OBB_FILE_PATH.matcher(obbFilePath);
-
- if (matcher.matches()) {
- int userId = UserHandle.getUserId(Binder.getCallingUid());
- String pathUserId = matcher.group(2);
- String pathPackageName = matcher.group(3);
- if ((pathUserId != null && Integer.parseInt(pathUserId) != userId)
- || (pathUserId == null && userId != mCurrentUserId)) {
- throw new SecurityException(
- "Path " + obbFilePath + "does not correspond to calling userId " + userId);
- }
- if (obbInfo != null && !obbInfo.packageName.equals(pathPackageName)) {
- throw new SecurityException("Path " + obbFilePath
- + " does not contain package name " + pathPackageName);
- }
- } else {
- throw new SecurityException("Invalid path to Obb file : " + obbFilePath);
- }
- }
-
@Override
public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
Objects.requireNonNull(rawPath, "rawPath cannot be null");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 168ec052e67d..4e24cf38fe73 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -790,6 +790,7 @@ public class AudioService extends IAudioService.Stub
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
private final Executor mAudioServerLifecycleExecutor;
+ private long mSysPropListenerNativeHandle;
private final List<Future> mScheduledPermissionTasks = new ArrayList();
private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -10640,7 +10641,7 @@ public class AudioService extends IAudioService.Stub
}, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
}
};
- mAudioSystem.listenForSystemPropertyChange(
+ mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO,
task);
} else {
@@ -14713,6 +14714,7 @@ public class AudioService extends IAudioService.Stub
@Override
/** @see AudioManager#permissionUpdateBarrier() */
public void permissionUpdateBarrier() {
+ mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
List<Future> snapshot;
synchronized (mScheduledPermissionTasks) {
snapshot = List.copyOf(mScheduledPermissionTasks);
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index d083c68c4c2c..5cabddea9c17 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -748,8 +748,12 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
return AudioSystem.setMasterMute(mute);
}
- public void listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
- AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+ public long listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
+ return AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+ }
+
+ public void triggerSystemPropertyUpdate(long handle) {
+ AudioSystem.triggerSystemPropertyUpdate(handle);
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index 38eb416ffdd8..ddea285d3564 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -109,7 +109,7 @@ public class DisplayControl {
/**
* Sets the HDR conversion mode for the device.
*
- * Returns the system preferred Hdr output type nn case when HDR conversion mode is
+ * Returns the system preferred HDR output type in case when HDR conversion mode is
* {@link android.hardware.display.HdrConversionMode#HDR_CONVERSION_SYSTEM}.
* Returns Hdr::INVALID in other cases.
* @hide
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 93bd92614403..acf4db30ba93 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -318,13 +318,16 @@ final class DisplayDeviceInfo {
*/
public Display.HdrCapabilities hdrCapabilities;
+ /** When true, all HDR capabilities are hidden from public APIs */
+ public boolean isForceSdr;
+
/**
* Indicates whether this display supports Auto Low Latency Mode.
*/
public boolean allmSupported;
/**
- * Indicates whether this display suppors Game content type.
+ * Indicates whether this display supports Game content type.
*/
public boolean gameContentTypeSupported;
@@ -516,6 +519,7 @@ final class DisplayDeviceInfo {
|| !Arrays.equals(supportedModes, other.supportedModes)
|| !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equals(hdrCapabilities, other.hdrCapabilities)
+ || isForceSdr != other.isForceSdr
|| allmSupported != other.allmSupported
|| gameContentTypeSupported != other.gameContentTypeSupported
|| densityDpi != other.densityDpi
@@ -560,6 +564,7 @@ final class DisplayDeviceInfo {
colorMode = other.colorMode;
supportedColorModes = other.supportedColorModes;
hdrCapabilities = other.hdrCapabilities;
+ isForceSdr = other.isForceSdr;
allmSupported = other.allmSupported;
gameContentTypeSupported = other.gameContentTypeSupported;
densityDpi = other.densityDpi;
@@ -603,6 +608,7 @@ final class DisplayDeviceInfo {
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
sb.append(", hdrCapabilities ").append(hdrCapabilities);
+ sb.append(", isForceSdr ").append(isForceSdr);
sb.append(", allmSupported ").append(allmSupported);
sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported);
sb.append(", density ").append(densityDpi);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 187caba4db03..1813c2eb3242 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -48,6 +48,7 @@ import static android.os.Process.ROOT_UID;
import static android.provider.Settings.Secure.RESOLUTION_MODE_FULL;
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
+import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
@@ -282,7 +283,7 @@ public final class DisplayManagerService extends SystemService {
@GuardedBy("mSyncRoot")
private int[] mUserDisabledHdrTypes = {};
@Display.HdrCapabilities.HdrType
- private int[] mSupportedHdrOutputType;
+ private int[] mSupportedHdrOutputTypes;
@GuardedBy("mSyncRoot")
private boolean mAreUserDisabledHdrTypesAllowed = true;
@@ -297,10 +298,10 @@ public final class DisplayManagerService extends SystemService {
// HDR conversion mode chosen by user
@GuardedBy("mSyncRoot")
private HdrConversionMode mHdrConversionMode = null;
- // Actual HDR conversion mode, which takes app overrides into account.
- private HdrConversionMode mOverrideHdrConversionMode = null;
+ // Whether app has disabled HDR conversion
+ private boolean mShouldDisableHdrConversion = false;
@GuardedBy("mSyncRoot")
- private int mSystemPreferredHdrOutputType = Display.HdrCapabilities.HDR_TYPE_INVALID;
+ private int mSystemPreferredHdrOutputType = HDR_TYPE_INVALID;
// The synchronization root for the display manager.
@@ -1407,7 +1408,8 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void setUserDisabledHdrTypesInternal(int[] userDisabledHdrTypes) {
+ @VisibleForTesting
+ void setUserDisabledHdrTypesInternal(int[] userDisabledHdrTypes) {
synchronized (mSyncRoot) {
if (userDisabledHdrTypes == null) {
Slog.e(TAG, "Null is not an expected argument to "
@@ -1425,6 +1427,7 @@ public final class DisplayManagerService extends SystemService {
if (Arrays.equals(mUserDisabledHdrTypes, userDisabledHdrTypes)) {
return;
}
+
String userDisabledFormatsString = "";
if (userDisabledHdrTypes.length != 0) {
userDisabledFormatsString = TextUtils.join(",",
@@ -1440,6 +1443,15 @@ public final class DisplayManagerService extends SystemService {
handleLogicalDisplayChangedLocked(display);
});
}
+ /** Note: it may be expected to reset the Conversion Mode when an HDR type is enabled
+ and the Conversion Mode is set to System Preferred. This is handled in the Settings
+ code because in the special case where HDR is indirectly disabled by Force SDR
+ Conversion, manually enabling HDR is not recognized as an action that reduces the
+ disabled HDR count. Thus, this case needs to be checked in the Settings code when we
+ know we're enabling an HDR mode. If we split checking for SystemConversion and
+ isForceSdr in two places, we may have duplicate calls to resetting to System Conversion
+ and get two black screens.
+ */
}
}
@@ -1452,19 +1464,20 @@ public final class DisplayManagerService extends SystemService {
return true;
}
- private void setAreUserDisabledHdrTypesAllowedInternal(
+ @VisibleForTesting
+ void setAreUserDisabledHdrTypesAllowedInternal(
boolean areUserDisabledHdrTypesAllowed) {
synchronized (mSyncRoot) {
if (mAreUserDisabledHdrTypesAllowed == areUserDisabledHdrTypesAllowed) {
return;
}
mAreUserDisabledHdrTypesAllowed = areUserDisabledHdrTypesAllowed;
- if (mUserDisabledHdrTypes.length == 0) {
- return;
- }
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
areUserDisabledHdrTypesAllowed ? 1 : 0);
+ if (mUserDisabledHdrTypes.length == 0) {
+ return;
+ }
int userDisabledHdrTypes[] = {};
if (!mAreUserDisabledHdrTypesAllowed) {
userDisabledHdrTypes = mUserDisabledHdrTypes;
@@ -1475,6 +1488,14 @@ public final class DisplayManagerService extends SystemService {
display.setUserDisabledHdrTypes(finalUserDisabledHdrTypes);
handleLogicalDisplayChangedLocked(display);
});
+ // When HDR conversion mode is set to SYSTEM, modification to
+ // areUserDisabledHdrTypesAllowed requires refreshing the HDR conversion mode to tell
+ // the system which HDR types it is not allowed to use.
+ if (getHdrConversionModeInternal().getConversionMode()
+ == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
+ setHdrConversionModeInternal(
+ new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ }
}
}
@@ -2348,7 +2369,7 @@ public final class DisplayManagerService extends SystemService {
final int preferredHdrOutputType =
hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_FORCE
? hdrConversionMode.getPreferredHdrOutputType()
- : Display.HdrCapabilities.HDR_TYPE_INVALID;
+ : HDR_TYPE_INVALID;
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.HDR_FORCE_CONVERSION_TYPE, preferredHdrOutputType);
}
@@ -2361,7 +2382,7 @@ public final class DisplayManagerService extends SystemService {
? Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.HDR_FORCE_CONVERSION_TYPE,
Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION)
- : Display.HdrCapabilities.HDR_TYPE_INVALID;
+ : HDR_TYPE_INVALID;
mHdrConversionMode = new HdrConversionMode(conversionMode, preferredHdrOutputType);
setHdrConversionModeInternal(mHdrConversionMode);
}
@@ -2498,22 +2519,38 @@ public final class DisplayManagerService extends SystemService {
});
}
+ /**
+ * Returns the HDR output types that are supported by the device's HDR conversion capabilities,
+ * stripping out any user-disabled HDR types if mAreUserDisabledHdrTypesAllowed is false.
+ */
@GuardedBy("mSyncRoot")
- private int[] getEnabledAutoHdrTypesLocked() {
- IntArray autoHdrOutputTypesArray = new IntArray();
+ @VisibleForTesting
+ int[] getEnabledHdrOutputTypesLocked() {
+ if (mAreUserDisabledHdrTypesAllowed) {
+ return getSupportedHdrOutputTypesInternal();
+ }
+ // Strip out all HDR formats that are currently user-disabled
+ IntArray enabledHdrOutputTypesArray = new IntArray();
for (int type : getSupportedHdrOutputTypesInternal()) {
- boolean isDisabled = false;
+ boolean isEnabled = true;
for (int disabledType : mUserDisabledHdrTypes) {
if (type == disabledType) {
- isDisabled = true;
+ isEnabled = false;
break;
}
}
- if (!isDisabled) {
- autoHdrOutputTypesArray.add(type);
+ if (isEnabled) {
+ enabledHdrOutputTypesArray.add(type);
}
}
- return autoHdrOutputTypesArray.toArray();
+ return enabledHdrOutputTypesArray.toArray();
+ }
+
+ @VisibleForTesting
+ int[] getEnabledHdrOutputTypes() {
+ synchronized (mSyncRoot) {
+ return getEnabledHdrOutputTypesLocked();
+ }
}
@GuardedBy("mSyncRoot")
@@ -2522,7 +2559,7 @@ public final class DisplayManagerService extends SystemService {
final int preferredHdrOutputType =
mode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM
? mSystemPreferredHdrOutputType : mode.getPreferredHdrOutputType();
- if (preferredHdrOutputType != Display.HdrCapabilities.HDR_TYPE_INVALID) {
+ if (preferredHdrOutputType != HDR_TYPE_INVALID) {
int[] hdrTypesWithLatency = mInjector.getHdrOutputTypesWithLatency();
return ArrayUtils.contains(hdrTypesWithLatency, preferredHdrOutputType);
}
@@ -2556,41 +2593,57 @@ public final class DisplayManagerService extends SystemService {
if (!mInjector.getHdrOutputConversionSupport()) {
return;
}
- int[] autoHdrOutputTypes = null;
+
synchronized (mSyncRoot) {
if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM
&& hdrConversionMode.getPreferredHdrOutputType()
- != Display.HdrCapabilities.HDR_TYPE_INVALID) {
+ != HDR_TYPE_INVALID) {
throw new IllegalArgumentException("preferredHdrOutputType must not be set if"
+ " the conversion mode is HDR_CONVERSION_SYSTEM");
}
mHdrConversionMode = hdrConversionMode;
storeHdrConversionModeLocked(mHdrConversionMode);
- // For auto mode, all supported HDR types are allowed except the ones specifically
- // disabled by the user.
+ // If the HDR conversion is HDR_CONVERSION_SYSTEM, all supported HDR types are allowed
+ // except the ones specifically disabled by the user.
+ int[] enabledHdrOutputTypes = null;
if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
- autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
+ enabledHdrOutputTypes = getEnabledHdrOutputTypesLocked();
}
int conversionMode = hdrConversionMode.getConversionMode();
int preferredHdrType = hdrConversionMode.getPreferredHdrOutputType();
+
// If the HDR conversion is disabled by an app through WindowManager.LayoutParams, then
// set HDR conversion mode to HDR_CONVERSION_PASSTHROUGH.
- if (mOverrideHdrConversionMode == null) {
+ if (mShouldDisableHdrConversion) {
+ conversionMode = HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
+ preferredHdrType = -1;
+ enabledHdrOutputTypes = null;
+ } else {
// HDR_CONVERSION_FORCE with HDR_TYPE_INVALID is used to represent forcing SDR type.
- // But, internally SDR is selected by using passthrough mode.
+ // But, internally SDR is forced by using passthrough mode and not reporting any
+ // HDR capabilities to apps.
if (conversionMode == HdrConversionMode.HDR_CONVERSION_FORCE
- && preferredHdrType == Display.HdrCapabilities.HDR_TYPE_INVALID) {
+ && preferredHdrType == HDR_TYPE_INVALID) {
conversionMode = HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
+ mLogicalDisplayMapper.forEachLocked(
+ logicalDisplay -> {
+ if (logicalDisplay.setIsForceSdr(true)) {
+ handleLogicalDisplayChangedLocked(logicalDisplay);
+ }
+ });
+ } else {
+ mLogicalDisplayMapper.forEachLocked(
+ logicalDisplay -> {
+ if (logicalDisplay.setIsForceSdr(false)) {
+ handleLogicalDisplayChangedLocked(logicalDisplay);
+ }
+ });
}
- } else {
- conversionMode = mOverrideHdrConversionMode.getConversionMode();
- preferredHdrType = mOverrideHdrConversionMode.getPreferredHdrOutputType();
- autoHdrOutputTypes = null;
}
mSystemPreferredHdrOutputType = mInjector.setHdrConversionMode(
- conversionMode, preferredHdrType, autoHdrOutputTypes);
+ conversionMode, preferredHdrType, enabledHdrOutputTypes);
}
}
@@ -2612,8 +2665,8 @@ public final class DisplayManagerService extends SystemService {
}
HdrConversionMode mode;
synchronized (mSyncRoot) {
- mode = mOverrideHdrConversionMode != null
- ? mOverrideHdrConversionMode
+ mode = mShouldDisableHdrConversion
+ ? new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH)
: mHdrConversionMode;
// Handle default: PASSTHROUGH. Don't include the system-preferred type.
if (mode == null
@@ -2621,8 +2674,6 @@ public final class DisplayManagerService extends SystemService {
return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH);
}
// Handle default or current mode: SYSTEM. Include the system preferred type.
- // mOverrideHdrConversionMode and mHdrConversionMode do not include the system
- // preferred type, it is kept separately in mSystemPreferredHdrOutputType.
if (mode == null
|| mode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
return new HdrConversionMode(
@@ -2633,10 +2684,10 @@ public final class DisplayManagerService extends SystemService {
}
private @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypesInternal() {
- if (mSupportedHdrOutputType == null) {
- mSupportedHdrOutputType = mInjector.getSupportedHdrOutputTypes();
+ if (mSupportedHdrOutputTypes == null) {
+ mSupportedHdrOutputTypes = mInjector.getSupportedHdrOutputTypes();
}
- return mSupportedHdrOutputType;
+ return mSupportedHdrOutputTypes;
}
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
@@ -2822,15 +2873,9 @@ public final class DisplayManagerService extends SystemService {
// HDR conversion is disabled in two cases:
// - HDR conversion introduces latency and minimal post-processing is requested
// - app requests to disable HDR conversion
- if (mOverrideHdrConversionMode == null && (disableHdrConversion
- || disableHdrConversionForLatency)) {
- mOverrideHdrConversionMode =
- new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH);
- setHdrConversionModeInternal(mHdrConversionMode);
- handleLogicalDisplayChangedLocked(display);
- } else if (mOverrideHdrConversionMode != null && !disableHdrConversion
- && !disableHdrConversionForLatency) {
- mOverrideHdrConversionMode = null;
+ boolean previousShouldDisableHdrConversion = mShouldDisableHdrConversion;
+ mShouldDisableHdrConversion = disableHdrConversion || disableHdrConversionForLatency;
+ if (previousShouldDisableHdrConversion != mShouldDisableHdrConversion) {
setHdrConversionModeInternal(mHdrConversionMode);
handleLogicalDisplayChangedLocked(display);
}
@@ -3509,9 +3554,9 @@ public final class DisplayManagerService extends SystemService {
}
int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
- int[] autoHdrTypes) {
+ int[] allowedHdrOutputTypes) {
return DisplayControl.setHdrConversionMode(conversionMode, preferredHdrOutputType,
- autoHdrTypes);
+ allowedHdrOutputTypes);
}
@Display.HdrCapabilities.HdrType
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5d55d1904f1b..5e42444ecf12 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -518,6 +518,7 @@ final class LogicalDisplay {
deviceInfo.supportedColorModes,
deviceInfo.supportedColorModes.length);
mBaseDisplayInfo.hdrCapabilities = deviceInfo.hdrCapabilities;
+ mBaseDisplayInfo.isForceSdr = deviceInfo.isForceSdr;
mBaseDisplayInfo.userDisabledHdrTypes = mUserDisabledHdrTypes;
mBaseDisplayInfo.minimalPostProcessingSupported =
deviceInfo.allmSupported || deviceInfo.gameContentTypeSupported;
@@ -899,6 +900,29 @@ final class LogicalDisplay {
}
/**
+ * Checks whether display is of the type where HDR settings are relevant, and then sets
+ * whether Force SDR conversion mode is active. isForceSdr is checked by the Display when
+ * returning HDR capabilities.
+ *
+ * @param isForceSdr Whether Force SDR conversion mode is active
+ * @return Whether Display Manager should call handleLogicalDisplayChangedLocked()
+ */
+ public boolean setIsForceSdr(boolean isForceSdr) {
+ int displayType = getDisplayInfoLocked().type;
+ boolean isTargetDisplayType = displayType == Display.TYPE_INTERNAL
+ || displayType == Display.TYPE_EXTERNAL
+ || displayType == Display.TYPE_OVERLAY;
+
+ boolean handleLogicalDisplayChangedLocked = false;
+ if (isTargetDisplayType && mBaseDisplayInfo.isForceSdr != isForceSdr) {
+ mBaseDisplayInfo.isForceSdr = isForceSdr;
+ mInfo.set(null);
+ handleLogicalDisplayChangedLocked = true;
+ }
+ return handleLogicalDisplayChangedLocked;
+ }
+
+ /**
* Swap the underlying {@link DisplayDevice} with the specified LogicalDisplay.
*
* @param targetDisplay The display with which to swap display-devices.
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6b3b5bde851b..67900f843063 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -251,7 +251,7 @@ public final class TvInputManagerService extends SystemService {
}
private void registerBroadcastReceivers() {
- PackageMonitor monitor = new PackageMonitor() {
+ PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
private void buildTvInputList(String[] packages) {
int userId = getChangingUserId();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index edd2fa9a4e44..6a7fc6dcf7cd 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -519,7 +519,7 @@ public class TvInteractiveAppManagerService extends SystemService {
}
private void registerBroadcastReceivers() {
- PackageMonitor monitor = new PackageMonitor() {
+ PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
private void buildTvInteractiveAppServiceList(String[] packages) {
int userId = getChangingUserId();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 20c5f02aaee2..a5cea34bf79d 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -1685,6 +1685,21 @@ public class BackgroundActivityStartController {
(state.mOriginatingPendingIntent != null));
}
+ if (finalVerdict.getRawCode() == BAL_ALLOW_GRACE_PERIOD) {
+ if (state.realCallerExplicitOptInOrAutoOptIn()
+ && state.mResultForRealCaller.allows()
+ && state.mResultForRealCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+ // real caller could allow with a different exemption
+ } else if (state.callerExplicitOptInOrAutoOptIn() && state.mResultForCaller.allows()
+ && state.mResultForCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+ // caller could allow with a different exemption
+ } else {
+ // log to determine grace period length distribution
+ Slog.wtf(TAG, "Activity start ONLY allowed by BAL_ALLOW_GRACE_PERIOD "
+ + finalVerdict.mMessage + ": " + state);
+ }
+ }
+
if (balImprovedMetrics()) {
if (shouldLogStats(finalVerdict, state)) {
String activityName;
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 4a870a3a5b6e..5f5365dca1e9 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
@@ -48,7 +47,6 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.IntArray;
-import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
@@ -138,22 +136,16 @@ class BackgroundLaunchProcessController {
if (appSwitchState == APP_SWITCH_ALLOW) {
// Allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
- final long now = SystemClock.uptimeMillis();
- if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
- || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
- // If activity is started and finished before stop app switch time, we should not
- // let app to be able to start background activity even it's in grace period.
- if (lastActivityLaunchTime > lastStopAppSwitchesTime
- || lastActivityFinishTime > lastStopAppSwitchesTime) {
+ if (lastActivityLaunchTime > lastStopAppSwitchesTime
+ || lastActivityFinishTime > lastStopAppSwitchesTime) {
+ final long now = SystemClock.uptimeMillis();
+ long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime,
+ lastActivityFinishTime);
+ if (timeSinceLastStartOrFinish < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
- "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+ "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period ("
+ + timeSinceLastStartOrFinish + "ms)");
}
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS
- + "ms grace period but also within stop app switch window");
- }
-
}
}
return BalVerdict.BLOCK;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index b982098fefa4..5eec0124a9e3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1325,6 +1325,11 @@ class ActiveAdmin {
pw.print("encryptionRequested=");
pw.println(encryptionRequested);
+ if (!Flags.policyEngineMigrationV2Enabled()) {
+ pw.print("mUsbDataSignaling=");
+ pw.println(mUsbDataSignalingEnabled);
+ }
+
pw.print("disableCallerId=");
pw.println(disableCallerId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 4beb6a8a3480..a08af72586ee 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -230,9 +230,11 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
- policyDefinition, userId)) {
- return;
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+ policyDefinition, userId)) {
+ return;
+ }
}
if (policyDefinition.isNonCoexistablePolicy()) {
@@ -352,7 +354,9 @@ final class DevicePolicyEngine {
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
+ }
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -496,9 +500,11 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
- policyDefinition, UserHandle.USER_ALL)) {
- return;
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+ policyDefinition, UserHandle.USER_ALL)) {
+ return;
+ }
}
// TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
// that honors the restriction once there's an API available
@@ -565,7 +571,9 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- decreasePolicySizeForAdmin(policyState, enforcingAdmin);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ decreasePolicySizeForAdmin(policyState, enforcingAdmin);
+ }
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
@@ -1731,23 +1739,25 @@ final class DevicePolicyEngine {
pw.println();
}
pw.decreaseIndent();
- pw.println();
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ pw.println();
- pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
- pw.println("Current admin policy size limit: " + mPolicySizeLimit);
- pw.println("Admin Policies size: ");
- for (int i = 0; i < mAdminPolicySize.size(); i++) {
- int userId = mAdminPolicySize.keyAt(i);
- pw.printf("User %d:\n", userId);
- pw.increaseIndent();
- for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
- pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
- admin));
- pw.println();
+ pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
+ pw.println("Current admin policy size limit: " + mPolicySizeLimit);
+ pw.println("Admin Policies size: ");
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ pw.printf("User %d:\n", userId);
+ pw.increaseIndent();
+ for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
+ pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
+ admin));
+ pw.println();
+ }
+ pw.decreaseIndent();
}
pw.decreaseIndent();
}
- pw.decreaseIndent();
}
}
@@ -2008,21 +2018,23 @@ final class DevicePolicyEngine {
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (mAdminPolicySize != null) {
- for (int i = 0; i < mAdminPolicySize.size(); i++) {
- int userId = mAdminPolicySize.keyAt(i);
- for (EnforcingAdmin admin : mAdminPolicySize.get(
- userId).keySet()) {
- serializer.startTag(/* namespace= */ null,
- TAG_ENFORCING_ADMIN_AND_SIZE);
- serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
- admin.saveToXml(serializer);
- serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
- serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
- serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
- mAdminPolicySize.get(userId).get(admin));
- serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
- serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ if (mAdminPolicySize != null) {
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ for (EnforcingAdmin admin : mAdminPolicySize.get(
+ userId).keySet()) {
+ serializer.startTag(/* namespace= */ null,
+ TAG_ENFORCING_ADMIN_AND_SIZE);
+ serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ admin.saveToXml(serializer);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+ mAdminPolicySize.get(userId).get(admin));
+ serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
+ }
}
}
}
@@ -2030,6 +2042,9 @@ final class DevicePolicyEngine {
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
serializer.attributeInt(
/* namespace= */ null, ATTR_POLICY_SUM_SIZE, mPolicySizeLimit);
@@ -2177,6 +2192,9 @@ final class DevicePolicyEngine {
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 470025a67dee..886ae7ad7e50 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1328,7 +1328,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Bundle prevRestrictions) {
resetCrossProfileIntentFiltersIfNeeded(userId, newRestrictions, prevRestrictions);
resetUserVpnIfNeeded(userId, newRestrictions, prevRestrictions);
- removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
+ if (Flags.deletePrivateSpaceUnderRestriction()) {
+ removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
+ }
}
private void resetUserVpnIfNeeded(
@@ -3693,6 +3695,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
revertTransferOwnershipIfNecessaryLocked();
+ if (!Flags.policyEngineMigrationV2Enabled()) {
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
}
// Check whether work apps were paused via suspension and unsuspend if necessary.
@@ -7151,7 +7156,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwnerAsUser(caller.getUserId());
- boolean isDoUser = caller.getUserId() == getDeviceOwnerUserId();
+ boolean isDoUser = Flags.headlessSingleUserFixes()
+ ? caller.getUserId() == getDeviceOwnerUserId()
+ : caller.getUserHandle().isSystem();
if (aliasChooser == null && isDoUser) {
synchronized (getLockObject()) {
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
@@ -8161,7 +8168,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// First check whether the admin is allowed to wipe the device/user/profile.
final String restriction;
boolean shouldFactoryReset = userId == UserHandle.USER_SYSTEM;
- if (getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (Flags.headlessSingleUserFixes() && getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
shouldFactoryReset = userId == getMainUserId();
}
@@ -8185,7 +8192,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
adminPackage,
userId)) {
// Legacy mode
- wipeDevice = getHeadlessDeviceOwnerModeForDeviceOwner()
+ wipeDevice = Flags.headlessSingleUserFixes()
+ && getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER ? isMainUser : isSystemUser;
} else {
// Explicit behaviour
@@ -9369,7 +9377,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) {
if (userId == UserHandle.USER_ALL) {
- if (getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (Flags.headlessDeviceOwnerDelegateSecurityLoggingBugFix()
+ && getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
userId = mOwners.getDeviceOwnerUserId();
} else {
@@ -11855,7 +11864,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
setBackwardsCompatibleAppRestrictions(
caller, packageName, restrictions, caller.getUserHandle());
- } else {
+ } else if (Flags.dmrhSetAppRestrictions()) {
final boolean isRoleHolder;
if (who != null) {
// DO or PO
@@ -11902,6 +11911,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
caller.getUserHandle());
});
}
+ } else {
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller,
+ DELEGATION_APP_RESTRICTIONS)));
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ mUserManager.setApplicationRestrictions(packageName, restrictions,
+ caller.getUserHandle());
+ });
}
DevicePolicyEventLogger
@@ -12434,6 +12452,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (packageList != null) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ for (String pkg : packageList) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ }
+ }
+
List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
if (enabledImes != null) {
@@ -13232,7 +13256,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return Bundle.EMPTY;
}
return policies.get(enforcingAdmin).getValue();
- } else {
+ } else if (Flags.dmrhSetAppRestrictions()) {
final boolean isRoleHolder;
if (who != null) {
// Caller is DO or PO. They cannot call this on parent
@@ -13275,6 +13299,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return bundle != null ? bundle : Bundle.EMPTY;
});
}
+
+ } else {
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller,
+ DELEGATION_APP_RESTRICTIONS)));
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
+ caller.getUserHandle());
+ // if no restrictions were saved, mUserManager.getApplicationRestrictions
+ // returns null, but DPM method should return an empty Bundle as per JavaDoc
+ return bundle != null ? bundle : Bundle.EMPTY;
+ });
}
}
@@ -14283,6 +14320,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
+ }
+
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
synchronized (getLockObject()) {
int affectedUser = getAffectedUser(parent);
@@ -14893,6 +14934,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ for (String pkg : packages) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ }
+ }
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
@@ -15173,7 +15219,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- if (parent) {
+ if (Flags.allowScreenBrightnessControlOnCope() && parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
}
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
@@ -15184,7 +15230,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
"Permission denial: device owners cannot update %1$s", setting));
}
int affectedUser;
- if (parent) {
+ if (Flags.allowScreenBrightnessControlOnCope() && parent) {
affectedUser = getProfileParentId(caller.getUserId());
} else {
affectedUser = caller.getUserId();
@@ -16776,11 +16822,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- final UserHandle user = UserHandle.of(userId);
- final String roleHolderPackage = getRoleHolderPackageNameOnUser(
- RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
- if (roleHolderPackage != null) {
- broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ final UserHandle user = UserHandle.of(userId);
+ final String roleHolderPackage = getRoleHolderPackageNameOnUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
+ if (roleHolderPackage != null) {
+ broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
+ }
}
}
});
@@ -16788,10 +16836,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
- CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
- MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
- caller.getUserId());
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ CallerIdentity caller = getCallerIdentity(admin, callerPackage);
+ enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
+ caller.getUserId());
+ } else {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
+ final CallerIdentity caller = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ }
return mOwners.getSystemUpdateInfo();
}
@@ -17335,10 +17391,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Nullable ComponentName componentName, @UserIdInt int callingUserId) {
synchronized (getLockObject()) {
int deviceOwnerUserId = -1;
- deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
- && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
- == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
- ? UserHandle.USER_SYSTEM : callingUserId;
+ if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
+ deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+ && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
+ == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
+ ? UserHandle.USER_SYSTEM : callingUserId;
+ } else {
+ deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+ && getHeadlessDeviceOwnerModeForDeviceOwner()
+ == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
+ ? UserHandle.USER_SYSTEM : callingUserId;
+ }
Slogf.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
callingUserId, deviceOwnerUserId);
// hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
@@ -18637,7 +18700,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Backup service has to be enabled on the main user in order for it to be enabled on
// secondary users.
- if (isDeviceOwner(caller) && getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (Flags.headlessSingleUserFixes() && isDeviceOwner(caller)
+ && getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
toggleBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
}
@@ -21378,7 +21442,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
+ } else {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller)
+ || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ }
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
@@ -21977,9 +22047,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
boolean isSingleUserMode;
- int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
- deviceAdmin, caller.getUserId());
- isSingleUserMode = headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
+ if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
+ int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
+ deviceAdmin, caller.getUserId());
+ isSingleUserMode =
+ headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
+ } else {
+ isSingleUserMode =
+ getHeadlessDeviceOwnerModeForDeviceOwner()
+ == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
+ }
if (Flags.headlessSingleMinTargetSdk()
&& mInjector.userManagerIsHeadlessSystemUserMode()
@@ -22378,17 +22455,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- synchronized (getLockObject()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
- /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
- caller.getPackageName(),
- caller.getUserId());
+ if (!Flags.policyEngineMigrationV2Enabled()) {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "USB data signaling can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
"USB data signaling cannot be disabled.");
- mDevicePolicyEngine.setGlobalPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- enforcingAdmin,
- new BooleanPolicyValue(enabled));
+ }
+
+ synchronized (getLockObject()) {
+ if (Flags.policyEngineMigrationV2Enabled()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+ caller.getPackageName(),
+ caller.getUserId());
+ Preconditions.checkState(canUsbDataSignalingBeDisabled(),
+ "USB data signaling cannot be disabled.");
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ enforcingAdmin,
+ new BooleanPolicyValue(enabled));
+ } else {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
+ if (admin.mUsbDataSignalingEnabled != enabled) {
+ admin.mUsbDataSignalingEnabled = enabled;
+ saveSettingsLocked(caller.getUserId());
+ updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
+ }
+ }
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22410,10 +22505,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.USB_DATA_SIGNALING,
- caller.getUserId());
- return enabled == null || enabled;
+ if (Flags.policyEngineMigrationV2Enabled()) {
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
+ } else {
+ synchronized (getLockObject()) {
+ // If the caller is an admin, return the policy set by itself. Otherwise
+ // return the device-wide policy.
+ if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
+ caller)) {
+ return getProfileOwnerOrDeviceOwnerLocked(
+ caller.getUserId()).mUsbDataSignalingEnabled;
+ } else {
+ return isUsbDataSignalingEnabledInternalLocked();
+ }
+ }
+ }
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -24766,6 +24875,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
@@ -24779,6 +24891,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return -1;
+ }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
@@ -24788,6 +24903,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
caller.getUserId());
@@ -24798,6 +24916,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public int getPolicySizeForAdmin(
String callerPackageName, android.app.admin.EnforcingAdmin admin) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return -1;
+ }
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
caller.getUserId());
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index a4ef629492e7..7ffd0eca9b96 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -20,7 +20,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.supervision.ISupervisionManager;
import android.content.Context;
-
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
@@ -28,7 +30,9 @@ import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-/** Service for handling system supervision. */
+/**
+ * Service for handling system supervision.
+ */
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
@@ -44,8 +48,20 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
@Override
- protected void dump(@NonNull FileDescriptor fd,
- @NonNull PrintWriter fout, @Nullable String[] args) {
+ public void onShellCommand(
+ @Nullable FileDescriptor in,
+ @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args,
+ @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new SupervisionServiceShellCommand(this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @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());
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
new file mode 100644
index 000000000000..3aba24a3d4a5
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class SupervisionServiceShellCommand extends ShellCommand {
+ private final SupervisionService mService;
+
+ public SupervisionServiceShellCommand(SupervisionService mService) {
+ this.mService = mService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "help": return help(pw);
+ case "is-enabled": return isEnabled(pw);
+ default: return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int help(PrintWriter pw) {
+ pw.println("Supervision service commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text");
+ pw.println(" is-enabled");
+ pw.println(" Is supervision enabled");
+ return 0;
+ }
+
+ private int isEnabled(PrintWriter pw) {
+ pw.println(mService.isSupervisionEnabled());
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ help(getOutPrintWriter());
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 026fcc486a92..8f9382729119 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -27,6 +27,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_D
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.server.display.ExternalDisplayPolicy.ENABLE_ON_CONNECT;
@@ -190,8 +191,8 @@ public class DisplayManagerServiceTest {
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
private static final long STANDARD_AND_CONNECTION_DISPLAY_EVENTS =
STANDARD_DISPLAY_EVENTS | DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED;
@@ -233,6 +234,8 @@ public class DisplayManagerServiceTest {
private UserManager mUserManager;
+ private int[] mAllowedHdrOutputTypes;
+
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@Override
@@ -251,11 +254,12 @@ public class DisplayManagerServiceTest {
displayAdapterListener, flags,
mMockedDisplayNotificationManager,
new LocalDisplayAdapter.Injector() {
- @Override
- public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
- return mSurfaceControlProxy;
- }
- });
+ @Override
+ public LocalDisplayAdapter.SurfaceControlProxy
+ getSurfaceControlProxy() {
+ return mSurfaceControlProxy;
+ }
+ });
}
@Override
@@ -315,7 +319,7 @@ public class DisplayManagerServiceTest {
@Override
int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
- int[] autoHdrTypes) {
+ int[] allowedHdrOutputTypes) {
mHdrConversionMode = conversionMode;
mPreferredHdrOutputType = preferredHdrOutputType;
return Display.HdrCapabilities.HDR_TYPE_INVALID;
@@ -1287,11 +1291,11 @@ public class DisplayManagerServiceTest {
.setUniqueId("uniqueId --- mirror display");
assertThrows(SecurityException.class, () -> {
localService.createVirtualDisplay(
- builder.build(),
- mMockAppToken /* callback */,
- null /* virtualDeviceToken */,
- mock(DisplayWindowPolicyController.class),
- PACKAGE_NAME);
+ builder.build(),
+ mMockAppToken /* callback */,
+ null /* virtualDeviceToken */,
+ mock(DisplayWindowPolicyController.class),
+ PACKAGE_NAME);
});
}
@@ -1425,7 +1429,7 @@ public class DisplayManagerServiceTest {
// The virtual display should not have FLAG_ALWAYS_UNLOCKED set.
assertEquals(0, (displayManager.getDisplayDeviceInfoInternal(displayId).flags
- & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED));
+ & DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED));
}
/**
@@ -1458,7 +1462,7 @@ public class DisplayManagerServiceTest {
// The virtual display should not have FLAG_PRESENTATION set.
assertEquals(0, (displayManager.getDisplayDeviceInfoInternal(displayId).flags
- & DisplayDeviceInfo.FLAG_PRESENTATION));
+ & DisplayDeviceInfo.FLAG_PRESENTATION));
}
@Test
@@ -2350,6 +2354,7 @@ public class DisplayManagerServiceTest {
HdrConversionMode.HDR_CONVERSION_FORCE,
Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION);
displayManager.setHdrConversionModeInternal(mode);
+
assertEquals(mode, displayManager.getHdrConversionModeSettingInternal());
assertEquals(mode.getConversionMode(), mHdrConversionMode);
assertEquals(mode.getPreferredHdrOutputType(), mPreferredHdrOutputType);
@@ -2394,6 +2399,86 @@ public class DisplayManagerServiceTest {
}
@Test
+ public void testSetAreUserDisabledHdrTypesAllowed_withFalse_whenHdrDisabled_stripsHdrType() {
+ DisplayManagerService displayManager = new DisplayManagerService(
+ mContext, new BasicInjector() {
+ @Override
+ int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
+ int[] allowedTypes) {
+ mAllowedHdrOutputTypes = allowedTypes;
+ return Display.HdrCapabilities.HDR_TYPE_INVALID;
+ }
+
+ // Overriding this method to capture the allowed HDR type
+ @Override
+ int[] getSupportedHdrOutputTypes() {
+ return new int[]{Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION};
+ }
+ });
+
+ // Setup: no HDR types disabled, userDisabledTypes allowed, system conversion
+ displayManager.setUserDisabledHdrTypesInternal(new int [0]);
+ displayManager.setAreUserDisabledHdrTypesAllowedInternal(true);
+ displayManager.setHdrConversionModeInternal(
+ new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+
+ assertEquals(1, mAllowedHdrOutputTypes.length);
+ assertTrue(Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION == mAllowedHdrOutputTypes[0]);
+
+ // Action: disable Dolby Vision, set userDisabledTypes not allowed
+ displayManager.setUserDisabledHdrTypesInternal(
+ new int [] {Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION});
+ displayManager.setAreUserDisabledHdrTypesAllowedInternal(false);
+
+ assertEquals(0, mAllowedHdrOutputTypes.length);
+ }
+
+ @Test
+ public void testGetEnabledHdrTypesLocked_whenTypesDisabled_stripsDisabledTypes() {
+ DisplayManagerService displayManager = new DisplayManagerService(
+ mContext, new BasicInjector() {
+ @Override
+ int[] getSupportedHdrOutputTypes() {
+ return new int[]{Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION};
+ }
+ });
+
+ displayManager.setUserDisabledHdrTypesInternal(new int [0]);
+ displayManager.setAreUserDisabledHdrTypesAllowedInternal(true);
+ int [] enabledHdrOutputTypes = displayManager.getEnabledHdrOutputTypes();
+ assertEquals(1, enabledHdrOutputTypes.length);
+ assertTrue(Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION == enabledHdrOutputTypes[0]);
+
+ displayManager.setAreUserDisabledHdrTypesAllowedInternal(false);
+ enabledHdrOutputTypes = displayManager.getEnabledHdrOutputTypes();
+ assertEquals(1, enabledHdrOutputTypes.length);
+ assertTrue(Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION == enabledHdrOutputTypes[0]);
+
+ displayManager.setUserDisabledHdrTypesInternal(
+ new int [] {Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION});
+ enabledHdrOutputTypes = displayManager.getEnabledHdrOutputTypes();
+ assertEquals(0, enabledHdrOutputTypes.length);
+ }
+
+ @Test
+ public void testSetHdrConversionModeInternal_isForceSdrIsUpdated() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ LogicalDisplay logicalDisplay =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+
+ displayManager.setHdrConversionModeInternal(
+ new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_FORCE, HDR_TYPE_INVALID));
+ assertTrue(logicalDisplay.getDisplayInfoLocked().isForceSdr);
+
+ displayManager.setHdrConversionModeInternal(
+ new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
+ assertFalse(logicalDisplay.getDisplayInfoLocked().isForceSdr);
+ }
+
+ @Test
public void testReturnsRefreshRateForDisplayAndSensor_proximitySensorSet() {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
@@ -3351,7 +3436,7 @@ public class DisplayManagerServiceTest {
}
private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
- Display.Mode[] modes) {
+ Display.Mode[] modes) {
FakeDisplayDevice displayDevice = new FakeDisplayDevice();
DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
displayDeviceInfo.supportedModes = modes;
@@ -3607,9 +3692,9 @@ public class DisplayManagerServiceTest {
public void setUserPreferredDisplayModeLocked(Display.Mode preferredMode) {
for (Display.Mode mode : mDisplayDeviceInfo.supportedModes) {
if (mode.matchesIfValid(
- preferredMode.getPhysicalWidth(),
- preferredMode.getPhysicalHeight(),
- preferredMode.getRefreshRate())) {
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight(),
+ preferredMode.getRefreshRate())) {
mPreferredMode = mode;
break;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 957ee06b6e27..598d3a3a9f8a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -23,6 +23,8 @@ import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -1414,6 +1416,49 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ public void testSynthesizedGestureEventsDoNotMoveMagnifierViewport() {
+ final EventCaptor eventCaptor = new EventCaptor();
+ mMgh.setNext(eventCaptor);
+
+ float centerX =
+ (INITIAL_MAGNIFICATION_BOUNDS.left + INITIAL_MAGNIFICATION_BOUNDS.width()) / 2.0f;
+ float centerY =
+ (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
+ float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
+ mFullScreenMagnificationController.setScaleAndCenter(
+ DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+ centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
+ centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
+
+ // Second finger down on trackpad starts a synthesized two-finger swipe with source
+ // mouse.
+ MotionEvent downEvent = motionEvent(centerX, centerY, ACTION_DOWN,
+ TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+ send(downEvent, InputDevice.SOURCE_MOUSE);
+ fastForward(20);
+
+ // Two-finger swipe creates a synthesized move event, and shouldn't impact magnifier
+ // viewport.
+ MotionEvent moveEvent = motionEvent(centerX - 42, centerY - 42, ACTION_MOVE,
+ TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+ send(moveEvent, InputDevice.SOURCE_MOUSE);
+ fastForward(20);
+
+ assertThat(mFullScreenMagnificationController.getCenterX(DISPLAY_0)).isEqualTo(centerX);
+ assertThat(mFullScreenMagnificationController.getCenterY(DISPLAY_0)).isEqualTo(centerY);
+
+ // The events were not consumed by magnifier.
+ assertThat(eventCaptor.mEvents.size()).isEqualTo(2);
+ assertThat(eventCaptor.mEvents.get(0).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+ assertThat(eventCaptor.mEvents.get(1).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+
+ final List<Integer> expectedActions = new ArrayList();
+ expectedActions.add(Integer.valueOf(ACTION_DOWN));
+ expectedActions.add(Integer.valueOf(ACTION_MOVE));
+ assertActionsInOrder(eventCaptor.mEvents, expectedActions);
+ }
+
+ @Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
@@ -2130,6 +2175,30 @@ public class FullScreenMagnificationGestureHandlerTest {
return MotionEvent.obtain(mLastDownTime, mClock.now(), action, x, y, 0);
}
+ private MotionEvent motionEvent(float x, float y, int action, int toolType,
+ int classification) {
+ // Create a generic motion event to populate the parameters.
+ MotionEvent event = motionEvent(x, y, action);
+ int pointerCount = event.getPointerCount();
+ MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
+ MotionEvent.PointerProperties[] properties =
+ new MotionEvent.PointerProperties[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ properties[i] = new MotionEvent.PointerProperties();
+ event.getPointerProperties(i, properties[i]);
+ properties[i].toolType = toolType;
+ coords[i] = new MotionEvent.PointerCoords();
+ event.getPointerCoords(i, coords[i]);
+ }
+ // Apply the custom classification.
+ return MotionEvent.obtain(event.getDownTime(), event.getEventTime(), action,
+ /*pointerCount=*/1, properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
+ event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(),
+ classification);
+ }
+
private MotionEvent mouseEvent(float x, float y, int action) {
return fromMouse(motionEvent(x, y, action));
}