Merge "Support (non-editable) display of DND with a filter != PRIORITY" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 4e6dacf..ddfd364 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7964,13 +7964,13 @@
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 @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
+ field 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 @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+ field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index e7ed8fb..ba16037 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3470,6 +3470,7 @@
public static interface VirtualDeviceManager.ActivityListener {
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.content.IntentSender);
method public void onDisplayEmpty(int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowShown(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method @Deprecated public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
method public default void onTopActivityChanged(int, @NonNull android.content.ComponentName, int);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6454e73..42f7615 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -597,19 +597,19 @@
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 @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 @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 @FlaggedApi("android.app.admin.flags.headless_device_owner_provisioning_fix_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
+ method @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 @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 @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 @@
}
public final class EnforcingAdmin implements android.os.Parcelable {
- 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);
+ ctor 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> {
@@ -1797,11 +1797,15 @@
public class InputSettings {
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") public static int getAccessibilityBounceKeysThreshold(@NonNull android.content.Context);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysDelay(@NonNull android.content.Context);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysTimeout(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") public static int getAccessibilitySlowKeysThreshold(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") public static boolean isAccessibilityMouseKeysEnabled(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") public static boolean isAccessibilityStickyKeysEnabled(@NonNull android.content.Context);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityBounceKeysThreshold(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityMouseKeysEnabled(@NonNull android.content.Context, boolean);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysDelay(@NonNull android.content.Context, int);
+ method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysTimeout(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilitySlowKeysThreshold(@NonNull android.content.Context, int);
method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityStickyKeysEnabled(@NonNull android.content.Context, boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e99ba84..7a36fbb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2706,14 +2706,9 @@
if (mAllowlistToken == null) {
mAllowlistToken = processAllowlistToken;
}
- if (Flags.secureAllowlistToken()) {
- // Propagate this token to all pending intents that are unmarshalled from the parcel,
- // or keep the one we're already propagating, if that's the case.
- if (!parcel.hasClassCookie(PendingIntent.class)) {
- parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
- }
- } else {
- // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ // Propagate this token to all pending intents that are unmarshalled from the parcel,
+ // or keep the one we're already propagating, if that's the case.
+ if (!parcel.hasClassCookie(PendingIntent.class)) {
parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
}
@@ -3333,28 +3328,22 @@
PendingIntent.addOnMarshaledListener(addedListener);
}
try {
- if (Flags.secureAllowlistToken()) {
- boolean mustClearCookie = false;
- if (!parcel.hasClassCookie(Notification.class)) {
- // This is the "root" notification, and not an "inner" notification (including
- // publicVersion or anything else that might be embedded in extras). So we want
- // to use its token for every inner notification (might be null).
- parcel.setClassCookie(Notification.class, mAllowlistToken);
- mustClearCookie = true;
- }
- try {
- // IMPORTANT: Add marshaling code in writeToParcelImpl as we
- // want to intercept all pending events written to the parcel.
- writeToParcelImpl(parcel, flags);
- } finally {
- if (mustClearCookie) {
- parcel.removeClassCookie(Notification.class, mAllowlistToken);
- }
- }
- } else {
+ boolean mustClearCookie = false;
+ if (!parcel.hasClassCookie(Notification.class)) {
+ // This is the "root" notification, and not an "inner" notification (including
+ // publicVersion or anything else that might be embedded in extras). So we want
+ // to use its token for every inner notification (might be null).
+ parcel.setClassCookie(Notification.class, mAllowlistToken);
+ mustClearCookie = true;
+ }
+ try {
// IMPORTANT: Add marshaling code in writeToParcelImpl as we
// want to intercept all pending events written to the parcel.
writeToParcelImpl(parcel, flags);
+ } finally {
+ if (mustClearCookie) {
+ parcel.removeClassCookie(Notification.class, mAllowlistToken);
+ }
}
synchronized (this) {
@@ -3371,13 +3360,9 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
- if (Flags.secureAllowlistToken()) {
- // Always use the same token as the root notification (might be null).
- IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
- parcel.writeStrongBinder(rootNotificationToken);
- } else {
- parcel.writeStrongBinder(mAllowlistToken);
- }
+ // Always use the same token as the root notification (might be null).
+ IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
+ parcel.writeStrongBinder(rootNotificationToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 02e492b..515c1c6 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -24,7 +24,6 @@
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;
@@ -54,9 +53,7 @@
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
- }
+ 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 c993671..00e67e6 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -31,9 +30,7 @@
public BundlePolicyValue(Bundle value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
- }
+ 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 a7a2f7d..f092b7b 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -31,9 +30,7 @@
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxComponentNameLength(value);
- }
+ 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 156512a..c0e435c 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,8 +16,6 @@
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;
@@ -185,13 +183,11 @@
/**
* 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 d31d8f2..1ddec17 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,10 +54,8 @@
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;
@@ -17809,7 +17807,6 @@
*/
@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 {
@@ -17827,7 +17824,6 @@
*/
@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 {
@@ -17846,13 +17842,9 @@
* @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 f70a53f..5f9bb9c 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,6 @@
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;
@@ -64,7 +61,6 @@
*
* @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 68b4ad8..ab32d46 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -19,7 +19,6 @@
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;
@@ -135,10 +134,8 @@
}
private void setPackagesInternal(Set<String> packages) {
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String p : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(p);
- }
+ 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 1a04f6c..226c576 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -25,7 +25,6 @@
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;
@@ -59,10 +58,8 @@
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
- }
+ 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 9e31a23..8fa21db 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -24,7 +24,6 @@
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;
@@ -55,9 +54,7 @@
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ 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 8b253a2..24c50b0 100644
--- a/core/java/android/app/admin/PackageSetPolicyValue.java
+++ b/core/java/android/app/admin/PackageSetPolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -32,10 +31,8 @@
public PackageSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- for (String packageName : value) {
- PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
- }
+ 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 6efe9ad..bb07c23 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -30,9 +29,7 @@
public StringPolicyValue(@NonNull String value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
- }
+ 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 9054287..16cfba4 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -21,7 +21,6 @@
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;
@@ -45,9 +44,7 @@
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
- }
+ 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 edbbd5b..d9bd77f 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -4,6 +4,7 @@
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
@@ -28,16 +29,6 @@
}
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"
@@ -77,13 +68,6 @@
}
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"
@@ -226,16 +210,6 @@
}
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)"
@@ -246,13 +220,6 @@
}
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"
@@ -270,16 +237,6 @@
}
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"
@@ -300,16 +257,6 @@
}
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"
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 0ee9026..8f609de 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -49,7 +49,7 @@
/**
* Creates an instance.
*
- * @param mService An interface to the backing service.
+ * @param service An interface to the backing service.
* @param context A {@link Context}.
* @hide
*/
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 7c674f9..767f52a 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -54,4 +54,13 @@
*/
void onActivityLaunchBlocked(int displayId, in ComponentName componentName, in UserHandle user,
in IntentSender intentSender);
+
+ /**
+ * Called when a secure surface is shown on the device.
+ *
+ * @param displayId The display ID on which the secure surface was shown.
+ * @param componentName The component name of the activity that showed the secure surface.
+ * @param user The user associated with the activity.
+ */
+ void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index b7bf2d1..de20a68 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -151,7 +151,24 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i)
+ .onSecureWindowShown(displayId, componentName, user);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
+
private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
new IVirtualDeviceSoundEffectListener.Stub() {
@Override
@@ -584,6 +601,12 @@
mActivityListener.onActivityLaunchBlocked(
displayId, componentName, user, intentSender));
}
+
+ public void onSecureWindowShown(int displayId, ComponentName componentName,
+ UserHandle user) {
+ mExecutor.execute(() ->
+ mActivityListener.onSecureWindowShown(displayId, componentName, user));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 40aa6837..cf34452 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1255,6 +1255,20 @@
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
default void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName,
@NonNull UserHandle user, @Nullable IntentSender intentSender) {}
+
+ /**
+ * Called when a window with a secure surface is shown on the device.
+ *
+ * @param displayId The display ID on which the window was shown.
+ * @param componentName The component name of the activity that showed the window.
+ * @param user The user associated with the activity.
+ *
+ * @see Display#FLAG_SECURE
+ * @see WindowManager.LayoutParams#FLAG_SECURE
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+ default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
+ @NonNull UserHandle user) {}
}
/**
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index c5d0caf22..8592ded 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -20,10 +20,12 @@
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG;
import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG;
+import static com.android.hardware.input.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
+import static com.android.hardware.input.Flags.keyboardRepeatKeys;
import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.input.flags.Flags.enableInputFilterRustImpl;
@@ -40,6 +42,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.sysprop.InputProperties;
+import android.view.ViewConfiguration;
/**
* InputSettings encapsulates reading and writing settings related to input
@@ -90,6 +93,30 @@
*/
public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
+ /**
+ * The minimum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_TIMEOUT_MILLIS = 150;
+
+ /**
+ * The maximum allowed repeat keys timeout before starting key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_TIMEOUT_MILLIS = 2000;
+
+ /**
+ * The minimum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MIN_KEY_REPEAT_DELAY_MILLIS = 20;
+
+ /**
+ * The maximum allowed repeat keys delay between successive key repeats.
+ * @hide
+ */
+ public static final int MAX_KEY_REPEAT_DELAY_MILLIS = 2000;
+
private InputSettings() {
}
@@ -767,4 +794,141 @@
Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether "Repeat keys" feature flag is enabled.
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ public static boolean isRepeatKeysFeatureFlagEnabled() {
+ return keyboardRepeatKeys();
+ }
+
+ /**
+ * Get Accessibility repeat keys timeout duration in milliseconds.
+ * The default key repeat timeout is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_TIMEOUT_MS}.
+ *
+ * @param context The application context
+ * @return The time duration for which a key should be pressed after
+ * which the pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysTimeout(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Get Accessibility repeat keys delay rate in milliseconds.
+ * The default key repeat delay is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_DELAY_MS}.
+ *
+ * @param context The application context
+ * @return Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ public static int getAccessibilityRepeatKeysDelay(@NonNull Context context) {
+ return Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat keys timeout duration in milliseconds.
+ *
+ * @param timeoutTimeMillis time duration for which a key should be pressed after which the
+ * pressed key will be repeated. The timeout must be between
+ * {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+ *
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysTimeout(@NonNull Context context,
+ int timeoutTimeMillis) {
+ if (timeoutTimeMillis < MIN_KEY_REPEAT_TIMEOUT_MILLIS
+ || timeoutTimeMillis > MAX_KEY_REPEAT_TIMEOUT_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys timeout should be in range ("
+ + MIN_KEY_REPEAT_TIMEOUT_MILLIS + ","
+ + MAX_KEY_REPEAT_TIMEOUT_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS, timeoutTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
+
+ /**
+ * Set Accessibility repeat key delay duration in milliseconds.
+ *
+ * @param delayTimeMillis Time duration between successive key repeats when a key is
+ * pressed down. The delay duration must be between
+ * {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+ * {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+ * <p>
+ * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+ * key on the physical keyboard is held down. This accessibility feature allows the user
+ * to configure the timeout before the key repeats begin as well as the delay
+ * between successive key repeats.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setAccessibilityRepeatKeysDelay(@NonNull Context context,
+ int delayTimeMillis) {
+ if (delayTimeMillis < MIN_KEY_REPEAT_DELAY_MILLIS
+ || delayTimeMillis > MAX_KEY_REPEAT_DELAY_MILLIS) {
+ throw new IllegalArgumentException(
+ "Provided repeat keys delay should be in range ("
+ + MIN_KEY_REPEAT_DELAY_MILLIS + ","
+ + MAX_KEY_REPEAT_DELAY_MILLIS + ")");
+ }
+ Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
+ UserHandle.USER_CURRENT);
+ }
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 83c4de3..077bd82 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -99,3 +99,10 @@
description: "Refactor ModifierShortcutManager internal representation of shortcuts."
bug: "358603902"
}
+
+flag {
+ name: "keyboard_repeat_keys"
+ namespace: "input"
+ description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
+ bug: "336585002"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 24f52d0..98904fe 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1974,7 +1974,7 @@
/**
* Activity Action: Show Notification Policy access settings.
* <p>
- * Users can grant and deny access to Notification Policy (DND / Priority Modes) configuration
+ * Users can grant and deny access to Notification Policy (DND / Modes) configuration
* from here. Managed profiles cannot grant Notification Policy access.
* See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
* details.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 682a89f..3d8d933 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1044,23 +1044,19 @@
rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
} else if (MANUAL_TAG.equals(tag)) {
- ZenRule manualRule = readRuleXml(parser);
- if (manualRule != null) {
- rt.manualRule = manualRule;
-
- // Manual rule may be present prior to modes_ui if it were on, but in that
- // case it would not have a set policy, so make note of the need to set
- // it up later.
- readManualRule = true;
- if (rt.manualRule.zenPolicy == null) {
- readManualRuleWithoutPolicy = true;
- }
+ rt.manualRule = readRuleXml(parser);
+ // Manual rule may be present prior to modes_ui if it were on, but in that
+ // case it would not have a set policy, so make note of the need to set
+ // it up later.
+ readManualRule = true;
+ if (rt.manualRule.zenPolicy == null) {
+ readManualRuleWithoutPolicy = true;
}
} else if (AUTOMATIC_TAG.equals(tag)
|| (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
- final ZenRule automaticRule = readRuleXml(parser);
- if (id != null && automaticRule != null) {
+ if (id != null) {
+ final ZenRule automaticRule = readRuleXml(parser);
automaticRule.id = id;
if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
String deletedRuleKey = deletedRuleKey(automaticRule);
@@ -1177,16 +1173,13 @@
out.endTag(null, ZEN_TAG);
}
+ @NonNull
public static ZenRule readRuleXml(TypedXmlPullParser parser) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
- rt.zenMode = tryParseZenMode(zen, -1);
- if (rt.zenMode == -1) {
- Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
- return null;
- }
+ rt.zenMode = tryParseZenMode(zen, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index ab29df3..a87e5c8 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -2095,9 +2095,7 @@
* {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than
* {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2125,9 +2123,7 @@
*
* @return {@code true} if the proxy is successfully unregistered.
*
- * @throws SecurityException if the app does not hold the
- * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
- * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+ * @throws SecurityException if the app does not hold the required permissions.
*
* @hide
*/
@@ -2180,8 +2176,8 @@
try {
return service.startFlashNotificationSequence(context.getOpPackageName(),
reason, mBinder);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification sequence", e);
return false;
}
}
@@ -2210,8 +2206,8 @@
try {
return service.stopFlashNotificationSequence(context.getOpPackageName());
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while stop flash notification sequence", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while stop flash notification sequence", e);
return false;
}
}
@@ -2238,8 +2234,8 @@
try {
return service.startFlashNotificationEvent(context.getOpPackageName(),
reason, reasonPkg);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while start flash notification event", re);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(LOG_TAG, "Error while start flash notification event", e);
return false;
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index bf79a2c..2de3ce8 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -157,13 +157,13 @@
@EnforcePermission("INJECT_EVENTS")
void injectInputEventToInputFilter(in InputEvent event);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean stopFlashNotificationSequence(String opPkg);
- @RequiresNoPermission
+ @EnforcePermission("MANAGE_ACCESSIBILITY")
boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
@RequiresNoPermission
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 2f515fe..3a008aa 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -323,14 +323,14 @@
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -339,14 +339,15 @@
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,7 +408,7 @@
@Nullable IRemoteInputConnection remoteInputConnection,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
final IInputMethodManager service = getService();
if (service == null) {
return -1;
@@ -416,7 +417,7 @@
service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
- imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 23d7732..2f649c2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -441,6 +442,36 @@
public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
/**
+ * Use async method for {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)},
+ * {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int, ResultReceiver)},
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} for apps targeting V+.
+ * <p>
+ * Apps can incorrectly rely on {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} method return type
+ * to interpret result of a request rather than relying on {@link ResultReceiver}. The return
+ * type of the method was never documented to have accurate info of visibility but few apps
+ * incorrectly rely on it.
+ * <p>
+ * Starting Android V, we use async calls into system_server which returns {@code true} if
+ * method call was made but return type doesn't guarantee execution.
+ * Apps targeting older versions will fallback to existing behavior of calling synchronous
+ * methods which had undocumented result in return type.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
+
+ /**
+ * Version-gating is guarded by bug-fix flag.
+ */
+ private static final boolean ASYNC_SHOW_HIDE_METHOD_ENABLED =
+ !Flags.compatchangeForZerojankproxy()
+ || CompatChanges.isChangeEnabled(USE_ASYNC_SHOW_HIDE_METHOD);
+
+ /**
* If {@code true}, avoid calling the
* {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
* by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus}
@@ -2246,6 +2277,8 @@
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
@@ -2315,6 +2348,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2383,7 +2418,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2426,7 +2462,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -2459,6 +2496,9 @@
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@link ResultReceiver} in
+ * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2487,6 +2527,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
@@ -2530,7 +2572,7 @@
return true;
} else {
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
- statsToken, flags, resultReceiver, reason);
+ statsToken, flags, resultReceiver, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2573,7 +2615,7 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(),
- statsToken, flags, null, reason);
+ statsToken, flags, null, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -3350,7 +3392,7 @@
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+ mImeDispatcher, ASYNC_SHOW_HIDE_METHOD_ENABLED);
} else {
res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3653,7 +3695,8 @@
statsToken,
HIDE_NOT_ALWAYS,
null,
- reason);
+ reason,
+ true /*async */);
}
}
@@ -3745,7 +3788,7 @@
IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
0 /* flags */, null /* resultReceiver */,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, true /* async */);
}
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e294ee2..bae8aff 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -136,3 +136,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "compatchange_for_zerojankproxy"
+ namespace: "input_method"
+ description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
+ bug: "352594277"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c53a0e1..f532934 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -318,7 +318,7 @@
String libraryFileName;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+ PackageManager.GET_META_DATA);
libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOGTAG, "Couldn't find package " + packageName);
@@ -479,7 +479,6 @@
newPackageInfo = pm.getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// Make sure that we fetch the current provider even if its not
// installed for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9f5ed65..21fbf9d 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -134,7 +134,8 @@
* Prints data on dumpsys.
*/
public void dump(PrintWriter pw) {
- pw.println("BrightnessSynchronizer");
+ pw.println("BrightnessSynchronizer:");
+ pw.println("-----------------------");
pw.println(" mLatestIntBrightness=" + mLatestIntBrightness);
pw.println(" mLatestFloatBrightness=" + mLatestFloatBrightness);
pw.println(" mCurrentUpdate=" + mCurrentUpdate);
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index ef6bece..837622f 100644
--- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -37,6 +37,7 @@
import android.util.proto.ProtoInputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.LogLevel;
import java.io.IOException;
@@ -48,6 +49,7 @@
public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance,
ProtoLogDataSource.TlsState,
ProtoLogDataSource.IncrementalState> {
+ private static final String DATASOURCE_NAME = "android.protolog";
private final Instance.TracingInstanceStartCallback mOnStart;
private final Runnable mOnFlush;
@@ -55,7 +57,13 @@
public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
Instance.TracingInstanceStopCallback onStop) {
- super("android.protolog");
+ this(onStart, onFlush, onStop, DATASOURCE_NAME);
+ }
+
+ @VisibleForTesting
+ public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
+ Instance.TracingInstanceStopCallback onStop, String dataSourceName) {
+ super(dataSourceName);
this.mOnStart = onStart;
this.mOnFlush = onFlush;
this.mOnStop = onStop;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b51678e..efbf887 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -76,10 +76,10 @@
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags, int lastClickToolType,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
/**
* A test API for CTS to request hiding the current soft input window, with the request origin
@@ -120,7 +120,8 @@
in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, int userId,
- in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 020b27e..19f8299 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -391,6 +391,7 @@
} // namespace android
+#ifndef _WIN32
using namespace android;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -407,3 +408,4 @@
return JNI_VERSION_1_6;
}
+#endif
diff --git a/core/res/res/drawable/ic_zen_priority_modes.xml b/core/res/res/drawable/ic_zen_priority_modes.xml
index 98de27b..e8691fc 100644
--- a/core/res/res/drawable/ic_zen_priority_modes.xml
+++ b/core/res/res/drawable/ic_zen_priority_modes.xml
@@ -20,6 +20,6 @@
android:viewportHeight="960"
android:tint="?android:attr/colorControlNormal">
<path
- android:pathData="M160,480v-80h320v80L160,480ZM480,880q-80,0 -153.5,-29.5T196,764l56,-56q47,44 106,68t122,24q133,0 226.5,-93.5T800,480q0,-133 -93.5,-226.5T480,160v-80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880Z"
- android:fillColor="@android:color/white"/>
+ android:fillColor="@android:color/white"
+ android:pathData="M480,720Q580,720 650,650Q720,580 720,480Q720,380 650,310Q580,240 480,240L480,480L310,650Q345,683 388.5,701.5Q432,720 480,720ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 4d5f4f0..027f530 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -37,6 +37,7 @@
<ListView
android:id="@+id/autofill_dataset_list"
android:layout_weight="1"
+ android:fadeScrollbars="false"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:drawSelectorOnTop="true"
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index ecd2f76..b157c95 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,8 +16,11 @@
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;
@@ -104,7 +107,14 @@
public void testMountBadPackageNameObb() throws Exception {
final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
String filePath = file.getAbsolutePath();
- mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ try {
+ mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+ fail("mountObb should throw an exception as package name is incorrect");
+ } catch (Exception ex) {
+ assertEquals("Path " + filePath
+ + " does not contain package name " + mContext.getPackageName(),
+ ex.getMessage());
+ }
}
/**
@@ -154,6 +164,48 @@
}
}
+ @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 throw an 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/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 766a758..f2f2b7ea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -58,18 +58,22 @@
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.app.Application;
import android.app.Instrumentation;
import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -116,7 +120,7 @@
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
static final String TAG = "SplitController";
- static final boolean ENABLE_SHELL_TRANSITIONS = true;
+ static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
// TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
// association. It's not set in WM Extensions nor Wm Jetpack library currently.
@@ -3309,4 +3313,17 @@
transactionRecord.apply(false /* shouldApplyIndependently */);
}
}
+
+ // TODO(b/207070762): cleanup with legacy app transition
+ private static boolean getShellTransitEnabled() {
+ try {
+ if (AppGlobals.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error getting system features");
+ }
+ return true;
+ }
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 9de10c0..470b7a2 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -138,3 +138,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_bubble_to_fullscreen"
+ namespace: "multitasking"
+ description: "Enable an option to move bubbles to fullscreen"
+ bug: "363326492"
+}
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 36d0a3c..a353db7 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -155,6 +155,8 @@
<string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
<string name="bubble_dismiss_text">Dismiss bubble</string>
+ <!-- Text used to move the bubble to fullscreen. [CHAR LIMIT=30] -->
+ <string name="bubble_fullscreen_text">Move to fullscreen</string>
<!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
<string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
<!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 694b1b0..f90b2aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -228,6 +228,13 @@
public void onDismissBubble(Bubble bubble) {
mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
}
+
+ @Override
+ public void onMoveToFullscreen(Bubble bubble) {
+ if (mTaskView != null) {
+ mTaskView.moveToFullscreen();
+ }
+ }
});
mHandleView.setOnClickListener(view -> {
mMenuViewController.showMenu(true /* animated */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 0d72998..5148107 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -29,6 +29,7 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -219,6 +220,21 @@
}
));
+ if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ Icon.createWithResource(resources,
+ R.drawable.desktop_mode_ic_handle_menu_fullscreen),
+ resources.getString(R.string.bubble_fullscreen_text),
+ tintColor,
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onMoveToFullscreen(bubble);
+ }
+ }
+ ));
+ }
+
return menuActions;
}
@@ -249,5 +265,10 @@
* Dismiss bubble and remove it from the bubble stack
*/
void onDismissBubble(Bubble bubble);
+
+ /**
+ * Move the bubble to fullscreen.
+ */
+ void onMoveToFullscreen(Bubble bubble);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index fad3dee..1929729 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -42,6 +42,7 @@
.setSourceCrop(crop)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
+ .setHintForSeamlessTransition(true)
.build()));
}
@@ -78,6 +79,9 @@
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
+ if (buffer.containsHdrLayers()) {
+ mTransaction.setDimmingEnabled(mScreenshot, false);
+ }
mTransaction.show(mScreenshot);
mTransaction.apply();
}
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 4b30ed0..1d16980 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
@@ -913,6 +913,7 @@
val intent = Intent(context, DesktopWallpaperActivity::class.java)
val options =
ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index a85188a..82c0aaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -118,6 +118,13 @@
mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
}
+ /**
+ * Moves the current task in taskview out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ mTaskViewTaskController.moveToFullscreen();
+ }
+
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mTaskViewTaskController.isUsingShellTransitions()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 9750d3e..0259701 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.taskview;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.annotation.NonNull;
@@ -256,6 +257,24 @@
mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct);
}
+ /**
+ * Moves the current task in TaskView out of the view and back to fullscreen.
+ */
+ public void moveToFullscreen() {
+ if (mTaskToken == null) return;
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setWindowingMode(mTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.setAlwaysOnTop(mTaskToken, false);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ mTaskViewTransitions.moveTaskViewToFullscreen(wct, this);
+ if (mListener != null) {
+ // Task is being "removed" from the clients perspective
+ mListener.onTaskRemovalStarted(mTaskInfo.taskId);
+ }
+ });
+ }
+
private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
final Binder launchCookie = new Binder();
mShellExecutor.execute(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 15fe7ab..39648f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -236,6 +236,12 @@
startNextTransition();
}
+ void moveTaskViewToFullscreen(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskViewTaskController taskView) {
+ mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+ startNextTransition();
+ }
+
/** Starts a new transition to make the given {@code taskView} visible. */
public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
setTaskViewVisible(taskView, visible, false /* reorder */);
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 507ad64..880e021 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -153,6 +153,22 @@
assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
)
+ val EDGE_RESIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("EDGE_RESIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val CORNER_RESIZE_TO_MINIMUM_SIZE =
FlickerConfigEntry(
scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
new file mode 100644
index 0000000..c3abf23
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeMouse : ResizeAppWithEdgeResize(InputMethod.MOUSE) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
new file mode 100644
index 0000000..86b0e6f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeStylus : ResizeAppWithEdgeResize(InputMethod.STYLUS) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
new file mode 100644
index 0000000..e6bb9ef
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeTouchpad : ResizeAppWithEdgeResize(InputMethod.TOUCHPAD) {
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+ @ExpectedScenarios(["EDGE_RESIZE"])
+ @Test
+ override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index b812c59..426f40b 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -16,10 +16,11 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -36,11 +37,12 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
+
@RunWith(BlockJUnit4ClassRunner::class)
@Postsubmit
open class MaximizeAppWindow
@JvmOverloads
-constructor(rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
+constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -57,6 +59,9 @@
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp.enterDesktopWithDrag(wmHelper, device)
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index 03d970f..42940a9 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -43,8 +43,8 @@
@JvmOverloads
constructor(
val rotation: Rotation = Rotation.ROTATION_0,
- val horizontalChange: Int = 50,
- val verticalChange: Int = -50,
+ val horizontalChange: Int = 200,
+ val verticalChange: Int = -200,
val appProperty: AppProperty = AppProperty.STANDARD
) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
new file mode 100644
index 0000000..d094967
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.MotionEventHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppWithEdgeResize
+@JvmOverloads
+constructor(
+ val inputMethod: MotionEventHelper.InputMethod,
+ val rotation: Rotation = Rotation.ROTATION_90
+) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val motionEventHelper = MotionEventHelper(instrumentation, inputMethod)
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(
+ Flags.enableDesktopWindowingMode()
+ && Flags.enableWindowingEdgeDragResize() && tapl.isTablet
+ )
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeRight() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.RIGHT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeLeft() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.LEFT
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeTop() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.TOP
+ )
+ }
+
+ @Test
+ open fun resizeAppWithEdgeResizeBottom() {
+ testApp.edgeResize(
+ wmHelper,
+ motionEventHelper,
+ DesktopModeAppHelper.Edges.BOTTOM
+ )
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
index 685a3ba..33242db 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -18,6 +18,8 @@
import android.app.Instrumentation
import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -26,9 +28,11 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@@ -37,7 +41,7 @@
@Postsubmit
open class SnapResizeAppWindowWithButton
@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +53,10 @@
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
index 8a4aa63..14eb779 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -18,6 +18,8 @@
import android.app.Instrumentation
import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -26,9 +28,11 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.window.flags.Flags
+import com.android.wm.shell.Utils
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@@ -37,7 +41,7 @@
@Postsubmit
open class SnapResizeAppWindowWithDrag
@JvmOverloads
-constructor(private val toLeft: Boolean = true, private val isResizable: Boolean = true) {
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -49,6 +53,10 @@
DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
}
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
index dee67f3..c0fafef 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell
import android.app.Instrumentation
+import android.platform.test.rule.EnsureDeviceSettingsRule
import android.platform.test.rule.NavigationModeRule
import android.platform.test.rule.PressHomeRule
import android.platform.test.rule.UnlockScreenRule
@@ -49,5 +50,6 @@
)
)
.around(PressHomeRule())
+ .around(EnsureDeviceSettingsRule())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4abaf5b..7305f49 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -68,6 +68,7 @@
"flickerlib-helpers",
"flickerlib-trace_processor_shell",
"platform-test-annotations",
+ "platform-test-rules",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
"launcher-helper-lib",
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
new file mode 100644
index 0000000..a36a4f8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
@@ -0,0 +1,2 @@
+# Window Manager > App Compat
+# Bug component: 970984
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 5b7521a..429774f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -22,7 +22,6 @@
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.FlakyTest
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,7 +35,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { pipApp.changeAspectRatio() }
+ transitions { pipApp.changeAspectRatio(wmHelper) }
}
@Presubmit
@@ -46,22 +45,6 @@
flicker.assertLayersEnd { this.visibleRegion(pipApp).isSameAspectRatio(1, 2) }
}
- @FlakyTest(bugId = 358278071)
- override fun hasAtMostOnePipDismissOverlayWindow() =
- super.hasAtMostOnePipDismissOverlayWindow()
-
- @FlakyTest(bugId = 358278071)
- override fun statusBarLayerPositionAtStartAndEnd() =
- super.statusBarLayerPositionAtStartAndEnd()
-
- @FlakyTest(bugId = 358278071)
- override fun taskBarWindowIsAlwaysVisible() =
- super.taskBarWindowIsAlwaysVisible()
-
- @FlakyTest(bugId = 358278071)
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0434742..c596ca3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -713,4 +713,16 @@
verify(mViewHandler).post(any());
verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
}
+
+ @Test
+ public void testMoveToFullscreen_callsTaskRemovalStarted() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskViewTaskController.moveToFullscreen();
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
}
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 0fda91d..0ade4db 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -141,3 +141,11 @@
description: "Enable override and recover routing table"
bug: "329043523"
}
+
+flag {
+ name: "nfc_watchdog"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable watchdog for the NFC system process"
+ bug: "362937338"
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index a543450..3011ce0 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-beta07"
+ extra["jetpackComposeVersion"] = "1.7.0-rc01"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 3507605..d01c0b9 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.5.2"
+agp = "8.6.0"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
new file mode 100644
index 0000000..50432f3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
deleted file mode 100644
index 9a97e46..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index 2c35211..a4b76b9 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 9f29c77..9a7f4b6 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.9-bin.zip
+distributionUrl=gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index e9153e3..f0c2ea6 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,13 +54,13 @@
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.3.0-beta05")
+ api("androidx.compose.material3:material3:1.3.0-rc01")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-beta07")
+ api("androidx.navigation:navigation-compose:2.8.0-rc01")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
similarity index 66%
rename from packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
rename to packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
index 06905379..1726036 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.shade.shared.model
+package com.android.settingslib.bluetooth.devicesettings;
-/** Enumerates all supported alignments of the shade. */
-sealed interface ShadeAlignment {
-
- /** Aligns the shade to the top. */
- data object Top : ShadeAlignment
-
- /** Aligns the shade to the bottom. */
- data object Bottom : ShadeAlignment
-}
+parcelable DeviceSettingsProviderServiceStatus;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
new file mode 100644
index 0000000..977849e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a device settings item in bluetooth device details config.
+ *
+ * @property enabled Whether the service is enabled.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingsProviderServiceStatus(
+ val enabled: Boolean,
+ val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+ override fun describeContents(): Int = 0
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.run {
+ writeBoolean(enabled)
+ writeBundle(extras)
+ }
+ }
+
+ companion object {
+ @JvmField
+ val CREATOR: Parcelable.Creator<DeviceSettingsProviderServiceStatus> =
+ object : Parcelable.Creator<DeviceSettingsProviderServiceStatus> {
+ override fun createFromParcel(parcel: Parcel) =
+ parcel.run {
+ DeviceSettingsProviderServiceStatus(
+ enabled = readBoolean(),
+ extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
+ )
+ }
+
+ override fun newArray(size: Int): Array<DeviceSettingsProviderServiceStatus?> {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
index d5efac9..1c0a1fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
@@ -18,10 +18,12 @@
import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus;
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener;
-oneway interface IDeviceSettingsProviderService {
- void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
- void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
- void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
+interface IDeviceSettingsProviderService {
+ DeviceSettingsProviderServiceStatus getServiceStatus();
+ oneway void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+ oneway void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+ oneway void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt
new file mode 100644
index 0000000..25080bc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings.data.model
+
+import android.os.IInterface
+
+/** Present a service connection status. */
+sealed interface ServiceConnectionStatus<out T : IInterface> {
+ /** Service is connecting. */
+ data object Connecting : ServiceConnectionStatus<Nothing>
+
+ /** Service is connected. */
+ data class Connected<T : IInterface>(val service: T) : ServiceConnectionStatus<T>
+
+ /** Service connection failed. */
+ data object Failed : ServiceConnectionStatus<Nothing>
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index d6b2862..33beb06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -22,7 +22,8 @@
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
-import com.android.internal.util.ConcurrentUtils
+import android.os.IInterface
+import android.util.Log
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
@@ -34,27 +35,28 @@
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.android.settingslib.bluetooth.devicesettings.data.model.ServiceConnectionStatus
import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -84,64 +86,132 @@
}
}
- private var config = AtomicReference<DeviceSettingsConfig?>(null)
- private var idToSetting = AtomicReference<Flow<Map<Int, DeviceSetting>>?>(null)
-
- /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
- suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? =
- config.computeIfAbsent {
- getConfigServiceBindingIntent(cachedDevice)
- .flatMapLatest { getService(it) }
- .map { it?.let { IDeviceSettingsConfigProviderService.Stub.asInterface(it) } }
- .map {
- it?.getDeviceSettingsConfig(
- deviceInfo { setBluetoothAddress(cachedDevice.address) }
- )
+ private var isServiceEnabled =
+ coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+ val states = getSettingsProviderServices()?.values ?: return@async false
+ combine(states) { it.toList() }
+ .mapNotNull { allStatus ->
+ if (allStatus.any { it is ServiceConnectionStatus.Failed }) {
+ false
+ } else if (allStatus.all { it is ServiceConnectionStatus.Connected }) {
+ allStatus
+ .filterIsInstance<
+ ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+ >()
+ .all { it.service.serviceStatus?.enabled == true }
+ } else {
+ null
+ }
}
.first()
}
+ private var config =
+ coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+ val intent =
+ tryGetEndpointFromMetadata(cachedDevice)?.toIntent()
+ ?: run {
+ Log.i(TAG, "Unable to read device setting metadata from $cachedDevice")
+ return@async null
+ }
+ getService(intent, IDeviceSettingsConfigProviderService.Stub::asInterface)
+ .flatMapConcat {
+ when (it) {
+ is ServiceConnectionStatus.Connected ->
+ flowOf(
+ it.service.getDeviceSettingsConfig(
+ deviceInfo { setBluetoothAddress(cachedDevice.address) }
+ )
+ )
+ ServiceConnectionStatus.Connecting -> flowOf()
+ ServiceConnectionStatus.Failed -> flowOf(null)
+ }
+ }
+ .first()
+ }
+
+ private val settingIdToItemMapping =
+ flow {
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return@flow
+ }
+ getSettingsProviderServices()
+ ?.values
+ ?.map {
+ it.flatMapLatest { status ->
+ when (status) {
+ is ServiceConnectionStatus.Connected ->
+ getDeviceSettingsFromService(cachedDevice, status.service)
+ else -> flowOf(emptyList())
+ }
+ }
+ }
+ ?.let { items -> combine(items) { it.toList().flatten() } }
+ ?.map { items -> items.associateBy { it.settingId } }
+ ?.let { emitAll(it) }
+ }
+ .shareIn(scope = coroutineScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
+ /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
+ suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? {
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return null
+ }
+ return readConfig()
+ }
+
/** Gets all device settings for the device. */
fun getDeviceSettingList(): Flow<List<DeviceSetting>> =
- getSettingIdToItemMapping().map { it.values.toList() }
+ settingIdToItemMapping.map { it.values.toList() }
/** Gets the device settings with the ID for the device. */
fun getDeviceSetting(@DeviceSettingId deviceSettingId: Int): Flow<DeviceSetting?> =
- getSettingIdToItemMapping().map { it[deviceSettingId] }
+ settingIdToItemMapping.map { it[deviceSettingId] }
/** Updates the device setting state for the device. */
suspend fun updateDeviceSettings(
@DeviceSettingId deviceSettingId: Int,
deviceSettingPreferenceState: DeviceSettingPreferenceState,
) {
- getDeviceSettingsConfig()?.let { config ->
+ if (!isServiceEnabled.await()) {
+ Log.w(TAG, "Service is disabled")
+ return
+ }
+ readConfig()?.let { config ->
(config.mainContentItems + config.moreSettingsItems)
.find { it.settingId == deviceSettingId }
?.let {
getSettingsProviderServices()
?.get(EndPoint(it.packageName, it.className, it.intentAction))
- ?.filterNotNull()
+ ?.filterIsInstance<
+ ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+ >()
?.first()
}
+ ?.service
?.updateDeviceSettings(
deviceInfo { setBluetoothAddress(cachedDevice.address) },
DeviceSettingState.Builder()
.setSettingId(deviceSettingId)
.setPreferenceState(deviceSettingPreferenceState)
- .build()
+ .build(),
)
}
}
+ private suspend fun readConfig(): DeviceSettingsConfig? = config.await()
+
private suspend fun getSettingsProviderServices():
- Map<EndPoint, StateFlow<IDeviceSettingsProviderService?>>? =
- getDeviceSettingsConfig()
+ Map<EndPoint, StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>>? =
+ readConfig()
?.let { config ->
(config.mainContentItems + config.moreSettingsItems).map {
EndPoint(
packageName = it.packageName,
className = it.className,
- intentAction = it.intentAction
+ intentAction = it.intentAction,
)
}
}
@@ -150,43 +220,22 @@
{ it },
{ endpoint ->
services.computeIfAbsent(endpoint) {
- getService(endpoint.toIntent())
- .map { service ->
- IDeviceSettingsProviderService.Stub.asInterface(service)
- }
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+ getService(
+ endpoint.toIntent(),
+ IDeviceSettingsProviderService.Stub::asInterface,
+ )
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ ServiceConnectionStatus.Connecting,
+ )
}
- }
+ },
)
- private fun getSettingIdToItemMapping(): Flow<Map<Int, DeviceSetting>> =
- idToSetting.computeIfAbsent {
- flow {
- getSettingsProviderServices()
- ?.values
- ?.map {
- it.flatMapLatest { service ->
- if (service != null) {
- getDeviceSettingsFromService(cachedDevice, service)
- } else {
- flowOf(emptyList())
- }
- }
- }
- ?.let { items -> combine(items) { it.toList().flatten() } }
- ?.map { items -> items.associateBy { it.settingId } }
- ?.let { emitAll(it) }
- }
- .shareIn(
- scope = coroutineScope,
- started = SharingStarted.WhileSubscribed(),
- replay = 1
- )
- }!!
-
private fun getDeviceSettingsFromService(
cachedDevice: CachedBluetoothDevice,
- service: IDeviceSettingsProviderService
+ service: IDeviceSettingsProviderService,
): Flow<List<DeviceSetting>> {
return callbackFlow {
val listener =
@@ -202,51 +251,28 @@
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
}
- private fun getService(intent: Intent): Flow<IBinder?> {
+ private fun <T : IInterface> getService(
+ intent: Intent,
+ transform: ((IBinder) -> T),
+ ): Flow<ServiceConnectionStatus<T>> {
return callbackFlow {
val serviceConnection =
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
- launch { send(service) }
+ launch { send(ServiceConnectionStatus.Connected(transform(service))) }
}
override fun onServiceDisconnected(name: ComponentName?) {
- launch { send(null) }
+ launch { send(ServiceConnectionStatus.Connecting) }
}
}
if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
- launch { send(null) }
+ launch { send(ServiceConnectionStatus.Failed) }
}
awaitClose { context.unbindService(serviceConnection) }
}
}
- private fun getConfigServiceBindingIntent(cachedDevice: CachedBluetoothDevice): Flow<Intent> {
- return callbackFlow {
- val listener =
- BluetoothAdapter.OnMetadataChangedListener { device, key, _ ->
- if (
- key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS &&
- cachedDevice.device == device
- ) {
- launch { tryGetEndpointFromMetadata(cachedDevice)?.let { send(it) } }
- }
- }
- bluetoothAdaptor.addOnMetadataChangedListener(
- cachedDevice.device,
- ConcurrentUtils.DIRECT_EXECUTOR,
- listener,
- )
- awaitClose {
- bluetoothAdaptor.removeOnMetadataChangedListener(cachedDevice.device, listener)
- }
- }
- .onStart { tryGetEndpointFromMetadata(cachedDevice)?.let { emit(it) } }
- .distinctUntilChanged()
- .map { it.toIntent() }
- .flowOn(backgroundCoroutineContext)
- }
-
private suspend fun tryGetEndpointFromMetadata(cachedDevice: CachedBluetoothDevice): EndPoint? =
withContext(backgroundCoroutineContext) {
val packageName =
@@ -257,29 +283,31 @@
val className =
BluetoothUtils.getFastPairCustomizedField(
cachedDevice.device,
- CONFIG_SERVICE_CLASS_NAME
+ CONFIG_SERVICE_CLASS_NAME,
) ?: return@withContext null
val intentAction =
BluetoothUtils.getFastPairCustomizedField(
cachedDevice.device,
- CONFIG_SERVICE_INTENT_ACTION
+ CONFIG_SERVICE_INTENT_ACTION,
) ?: return@withContext null
EndPoint(packageName, className, intentAction)
}
- private inline fun <T> AtomicReference<T?>.computeIfAbsent(producer: () -> T): T? =
- get() ?: producer().let { compareAndExchange(null, it) ?: it }
-
private inline fun deviceInfo(block: DeviceInfo.Builder.() -> Unit): DeviceInfo {
return DeviceInfo.Builder().apply { block() }.build()
}
companion object {
+ const val TAG = "DeviceSettingSrvConn"
const val METADATA_FAST_PAIR_CUSTOMIZED_FIELDS: Int = 25
const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME"
const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS"
const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION"
- val services = ConcurrentHashMap<EndPoint, StateFlow<IDeviceSettingsProviderService?>>()
+ val services =
+ ConcurrentHashMap<
+ EndPoint,
+ StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>,
+ >()
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt
new file mode 100644
index 0000000..aa22fac
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingsProviderServiceStatusTest {
+
+ @Test
+ fun parcelOperation() {
+ val item =
+ DeviceSettingsProviderServiceStatus(
+ enabled = true,
+ extras = Bundle().apply { putString("key1", "value1") },
+ )
+
+ val fromParcel = writeAndRead(item)
+
+ assertThat(fromParcel.enabled).isEqualTo(item.enabled)
+ assertThat(fromParcel.extras.getString("key1")).isEqualTo(item.extras.getString("key1"))
+ }
+
+ private fun writeAndRead(
+ item: DeviceSettingsProviderServiceStatus
+ ): DeviceSettingsProviderServiceStatus {
+ val parcel = Parcel.obtain()
+ item.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ return DeviceSettingsProviderServiceStatus.CREATOR.createFromParcel(parcel)
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 95ee46e..ce155b5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -33,6 +33,7 @@
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
@@ -47,10 +48,8 @@
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -59,12 +58,9 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
@@ -85,9 +81,6 @@
@Mock private lateinit var configService: IDeviceSettingsConfigProviderService.Stub
@Mock private lateinit var settingProviderService1: IDeviceSettingsProviderService.Stub
@Mock private lateinit var settingProviderService2: IDeviceSettingsProviderService.Stub
- @Captor
- private lateinit var metadataChangeCaptor:
- ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
private lateinit var underTest: DeviceSettingRepository
private val testScope = TestScope()
@@ -153,6 +146,12 @@
fun getDeviceSettingsConfig_withMetadata_success() {
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
val config = underTest.getDeviceSettingsConfig(cachedDevice)
@@ -161,32 +160,40 @@
}
@Test
- fun getDeviceSettingsConfig_waitMetadataChange_success() {
+ fun getDeviceSettingsConfig_noMetadata_returnNull() {
+ testScope.runTest {
+ `when`(
+ bluetoothDevice.getMetadata(
+ DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+ .thenReturn("".toByteArray())
+ `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+
+ val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+ assertThat(config).isNull()
+ }
+ }
+
+ @Test
+ fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
testScope.runTest {
`when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
- `when`(
- bluetoothDevice.getMetadata(
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
- .thenReturn("".toByteArray())
-
- var config: DeviceSettingConfigModel? = null
- val job = launch { config = underTest.getDeviceSettingsConfig(cachedDevice) }
- delay(1000)
- verify(bluetoothAdapter)
- .addOnMetadataChangedListener(
- eq(bluetoothDevice), any(), metadataChangeCaptor.capture())
- metadataChangeCaptor.value.onMetadataChanged(
- bluetoothDevice,
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
- BLUETOOTH_DEVICE_METADATA.toByteArray(),
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(false)
)
- `when`(
- bluetoothDevice.getMetadata(
- DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
- .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
- job.join()
- assertConfig(config!!, DEVICE_SETTING_CONFIG)
+ val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+ assertThat(config).isNull()
}
}
@@ -212,6 +219,12 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
}
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
var setting: DeviceSettingModel? = null
underTest
@@ -234,6 +247,12 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
}
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
var setting: DeviceSettingModel? = null
underTest
@@ -256,6 +275,12 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP))
}
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
var setting: DeviceSettingModel? = null
underTest
@@ -299,6 +324,12 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
}
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
var setting: DeviceSettingModel? = null
underTest
@@ -331,6 +362,12 @@
.getArgument<IDeviceSettingsListener>(1)
.onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
}
+ `when`(settingProviderService1.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
+ `when`(settingProviderService2.serviceStatus).thenReturn(
+ DeviceSettingsProviderServiceStatus(true)
+ )
var setting: DeviceSettingModel? = null
underTest
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index c2cf6e1..c333a7a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -16,7 +16,6 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
-import android.content.Context;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.TouchDelegate;
@@ -43,16 +42,14 @@
private final int mLargeTextSize;
private final AccessibilityMenuService mService;
- private final LayoutInflater mInflater;
private final List<A11yMenuShortcut> mShortcutDataList;
private final ShortcutDrawableUtils mShortcutDrawableUtils;
public A11yMenuAdapter(
AccessibilityMenuService service,
- Context displayContext, List<A11yMenuShortcut> shortcutDataList) {
+ List<A11yMenuShortcut> shortcutDataList) {
this.mService = service;
this.mShortcutDataList = shortcutDataList;
- mInflater = LayoutInflater.from(displayContext);
mShortcutDrawableUtils = new ShortcutDrawableUtils(service);
@@ -78,7 +75,8 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
- convertView = mInflater.inflate(R.layout.grid_item, parent, false);
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.grid_item, parent, false);
configureShortcutSize(convertView,
A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService));
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index de3c472..448472d 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -51,6 +51,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.UiContext;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.Flags;
@@ -101,7 +102,6 @@
};
private final AccessibilityMenuService mService;
- private final WindowManager mWindowManager;
private final DisplayManager mDisplayManager;
private ViewGroup mLayout;
private WindowManager.LayoutParams mLayoutParameter;
@@ -111,7 +111,6 @@
public A11yMenuOverlayLayout(AccessibilityMenuService service) {
mService = service;
- mWindowManager = mService.getSystemService(WindowManager.class);
mDisplayManager = mService.getSystemService(DisplayManager.class);
configureLayout();
mHandler = new Handler(Looper.getMainLooper());
@@ -134,8 +133,7 @@
int lastVisibilityState = View.GONE;
if (mLayout != null) {
lastVisibilityState = mLayout.getVisibility();
- mWindowManager.removeView(mLayout);
- mLayout = null;
+ clearLayout();
}
if (mLayoutParameter == null) {
@@ -143,14 +141,15 @@
}
final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- final Context context = mService.createDisplayContext(display).createWindowContext(
- TYPE_ACCESSIBILITY_OVERLAY, null);
- mLayout = new A11yMenuFrameLayout(context);
- updateLayoutPosition();
- inflateLayoutAndSetOnTouchListener(mLayout, context);
- mA11yMenuViewPager = new A11yMenuViewPager(mService, context);
+ final Context uiContext = mService.createWindowContext(
+ display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null);
+ final WindowManager windowManager = uiContext.getSystemService(WindowManager.class);
+ mLayout = new A11yMenuFrameLayout(uiContext);
+ updateLayoutPosition(uiContext);
+ inflateLayoutAndSetOnTouchListener(mLayout, uiContext);
+ mA11yMenuViewPager = new A11yMenuViewPager(mService);
mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex);
- mWindowManager.addView(mLayout, mLayoutParameter);
+ windowManager.addView(mLayout, mLayoutParameter);
mLayout.setVisibility(lastVisibilityState);
mA11yMenuViewPager.updateFooterState();
@@ -159,7 +158,11 @@
public void clearLayout() {
if (mLayout != null) {
- mWindowManager.removeView(mLayout);
+ WindowManager windowManager =
+ mLayout.getContext().getSystemService(WindowManager.class);
+ if (windowManager != null) {
+ windowManager.removeView(mLayout);
+ }
mLayout.setOnTouchListener(null);
mLayout = null;
}
@@ -170,8 +173,11 @@
if (mLayout == null || mLayoutParameter == null) {
return;
}
- updateLayoutPosition();
- mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+ updateLayoutPosition(mLayout.getContext());
+ WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class);
+ if (windowManager != null) {
+ windowManager.updateViewLayout(mLayout, mLayoutParameter);
+ }
}
private void initLayoutParams() {
@@ -183,8 +189,8 @@
mLayoutParameter.setTitle(mService.getString(R.string.accessibility_menu_service_name));
}
- private void inflateLayoutAndSetOnTouchListener(ViewGroup view, Context displayContext) {
- LayoutInflater inflater = LayoutInflater.from(displayContext);
+ private void inflateLayoutAndSetOnTouchListener(ViewGroup view, @UiContext Context uiContext) {
+ LayoutInflater inflater = LayoutInflater.from(uiContext);
inflater.inflate(R.layout.paged_menu, view);
view.setOnTouchListener(mService);
}
@@ -238,7 +244,11 @@
}
/** Updates a11y menu layout position by configuring layout params. */
- private void updateLayoutPosition() {
+ private void updateLayoutPosition(@UiContext @NonNull Context uiContext) {
+ WindowManager windowManager = uiContext.getSystemService(WindowManager.class);
+ if (windowManager == null) {
+ return;
+ }
final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
final Configuration configuration = mService.getResources().getConfiguration();
final int orientation = configuration.orientation;
@@ -276,14 +286,13 @@
mLayoutParameter.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayout.setBackgroundResource(R.drawable.shadow_0deg);
}
-
// Adjusts the y position of a11y menu layout to make the layout not to overlap bottom
// navigation bar window.
- updateLayoutByWindowInsetsIfNeeded();
+ updateLayoutByWindowInsetsIfNeeded(windowManager);
mLayout.setOnApplyWindowInsetsListener(
(view, insets) -> {
- if (updateLayoutByWindowInsetsIfNeeded()) {
- mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+ if (updateLayoutByWindowInsetsIfNeeded(windowManager)) {
+ windowManager.updateViewLayout(mLayout, mLayoutParameter);
}
return view.onApplyWindowInsets(insets);
});
@@ -295,9 +304,9 @@
* This method adjusts the layout position and size to
* make a11y menu not to overlap navigation bar window.
*/
- private boolean updateLayoutByWindowInsetsIfNeeded() {
+ private boolean updateLayoutByWindowInsetsIfNeeded(@NonNull WindowManager windowManager) {
boolean shouldUpdateLayout = false;
- WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+ WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
int xOffset = max(windowInsets.left, windowInsets.right);
@@ -396,7 +405,7 @@
}
private class A11yMenuFrameLayout extends FrameLayout {
- A11yMenuFrameLayout(@NonNull Context context) {
+ A11yMenuFrameLayout(@UiContext @NonNull Context context) {
super(context);
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index 08bbf19..a29ce82 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -146,12 +146,8 @@
/** The container layout for a11y menu. */
private ViewGroup mA11yMenuLayout;
- /** Display context for inflating views. */
- private Context mDisplayContext;
-
- public A11yMenuViewPager(AccessibilityMenuService service, Context displayContext) {
+ public A11yMenuViewPager(AccessibilityMenuService service) {
this.mService = service;
- this.mDisplayContext = displayContext;
}
/**
@@ -289,7 +285,8 @@
footerLayout.getLayoutParams().height =
(int) (footerLayout.getLayoutParams().height / densityScale);
// Adjust the view pager height for system bar and display cutout insets.
- WindowManager windowManager = mService.getSystemService(WindowManager.class);
+ WindowManager windowManager = mA11yMenuLayout.getContext()
+ .getSystemService(WindowManager.class);
WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
index 43ec956..152ff68 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
@@ -57,7 +57,7 @@
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
A11yMenuAdapter adapter = new A11yMenuAdapter(
- mService, holder.itemView.getContext(), mShortcutList.get(position));
+ mService, mShortcutList.get(position));
GridView gridView = (GridView) holder.itemView;
gridView.setNumColumns(A11yMenuViewPager.GridViewParams.getGridColumnCount(mService));
gridView.setAdapter(adapter);
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1ce1716..cf13621 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1356,13 +1356,6 @@
}
flag {
- name: "new_picker_ui"
- namespace: "systemui"
- description: "Enables the BC25 design of the customization picker UI."
- bug: "339081035"
-}
-
-flag {
namespace: "systemui"
name: "settings_ext_register_content_observer_on_bg_thread"
description: "Register content observer in callback flow APIs on background thread in SettingsProxyExt."
@@ -1373,6 +1366,16 @@
}
flag {
+ name: "notify_password_text_view_user_activity_in_background"
+ namespace: "systemui"
+ description: "Decide whether to notify the user activity in password text view, to power manager in the background thread."
+ bug: "346882515"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "face_message_defer_update"
namespace: "systemui"
description: "Only analyze the last n frames when determining whether to defer a face auth help message like low light"
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
index 2b1268e..5b368df 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
index 94b5db2..74ce4bb 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.communal.ui.compose.CommunalScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
index bc3fef1..871ade9 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.ui.composable.GoneScene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 72965fb..bfeaf92 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -27,7 +27,7 @@
import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
index 9b736b8..c58df35 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.notifications.ui.composable.NotificationsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
index ee1f525..d55210d 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.qs.ui.composable.QuickSettingsScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
index 3d7401d..5bb6ae4 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
@@ -17,7 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.qs.ui.composable.QuickSettingsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
index c655d6b..186914f 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
@@ -16,7 +16,7 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.ui.composable.ShadeScene
import dagger.Binds
import dagger.Module
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index d164eab..9f78d69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -528,7 +528,7 @@
// Update state whenever currentSceneKey has changed.
LaunchedEffect(state, currentSceneKey) {
if (currentSceneKey != state.transitionState.currentScene) {
- state.setTargetScene(currentSceneKey, coroutineScope = this)
+ state.setTargetScene(currentSceneKey, animationScope = this)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 270d751..c5bb33c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -34,7 +34,7 @@
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -57,7 +57,7 @@
private val actionsViewModelFactory: BouncerSceneActionsViewModel.Factory,
private val contentViewModelFactory: BouncerSceneContentViewModel.Factory,
private val dialogFactory: BouncerDialogFactory,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Bouncer
private val actionsViewModel: BouncerSceneActionsViewModel by lazy {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 54ffcf4..f658169 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -29,7 +29,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
@@ -46,7 +46,7 @@
private val dialogFactory: SystemUIDialogFactory,
private val interactionHandler: WidgetInteractionHandler,
private val widgetSection: CommunalAppWidgetSection,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Communal
override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 2029e9e..5f600d3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -22,13 +22,13 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneActionsViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -40,7 +40,7 @@
constructor(
actionsViewModelFactory: LockscreenSceneActionsViewModel.Factory,
private val lockscreenContent: Lazy<LockscreenContent>,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Lockscreen
private val actionsViewModel: LockscreenSceneActionsViewModel by lazy {
@@ -70,7 +70,7 @@
lockscreenContent: Lazy<LockscreenContent>,
modifier: Modifier = Modifier,
) {
- animateSceneFloatAsState(
+ animateContentFloatAsState(
value = QuickSettings.SharedValues.SquishinessValues.LockscreenSceneStarting,
key = QuickSettings.SharedValues.TilesSquishiness,
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 0eeb79b..97d89a2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -93,7 +93,7 @@
// Update state whenever currentSceneKey has changed.
LaunchedEffect(state, currentScene) {
if (currentScene != state.transitionState.currentScene) {
- state.setTargetScene(currentScene, coroutineScope = this)
+ state.setTargetScene(currentScene, animationScope = this)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 37888f2..e4c611e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -27,20 +27,17 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
@SysUISingleton
@@ -48,9 +45,7 @@
@Inject
constructor(
private val actionsViewModelFactory: NotificationsShadeOverlayActionsViewModel.Factory,
- private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
- private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
- private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+ private val contentViewModelFactory: NotificationsShadeOverlayContentViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -72,19 +67,22 @@
override fun ContentScope.Content(
modifier: Modifier,
) {
+ val viewModel =
+ rememberViewModel("NotificationsShadeOverlay-viewModel") {
+ contentViewModelFactory.create()
+ }
+ val placeholderViewModel =
+ rememberViewModel("NotificationsShadeOverlay-notifPlaceholderViewModel") {
+ viewModel.notificationsPlaceholderViewModelFactory.create()
+ }
+
OverlayShade(
modifier = modifier,
- viewModelFactory = overlayShadeViewModelFactory,
- lockscreenContent = { Optional.empty() },
+ onScrimClicked = viewModel::onScrimClicked,
) {
Column {
- val placeholderViewModel =
- rememberViewModel("NotificationsShadeOverlay") {
- notificationsPlaceholderViewModelFactory.create()
- }
-
ExpandedShadeHeader(
- viewModelFactory = shadeHeaderViewModelFactory,
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
createTintedIconManager = tintedIconManagerFactory::create,
createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
statusBarIconController = statusBarIconController,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index e9c96ea..ea3f066 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -27,24 +27,21 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -53,7 +50,6 @@
@Inject
constructor(
private val actionsViewModelFactory: NotificationsShadeSceneActionsViewModel.Factory,
- private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -61,8 +57,7 @@
private val statusBarIconController: StatusBarIconController,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
- private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.NotificationsShade
@@ -88,8 +83,7 @@
OverlayShade(
modifier = modifier,
- viewModelFactory = overlayShadeViewModelFactory,
- lockscreenContent = lockscreenContent,
+ onScrimClicked = {},
) {
Column {
ExpandedShadeHeader(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d372577..373383f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -98,7 +98,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
@@ -113,9 +113,11 @@
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
/** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class QuickSettingsScene
@Inject
@@ -130,7 +132,7 @@
private val statusBarIconController: StatusBarIconController,
private val mediaCarouselController: MediaCarouselController,
@Named(MediaModule.QS_PANEL) private val mediaHost: MediaHost,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.QuickSettings
private val actionsViewModel: QuickSettingsSceneActionsViewModel by lazy {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index fa37729..988c712 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -16,14 +16,32 @@
package com.android.systemui.qs.ui.composable
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.panels.ui.compose.EditMode
+import com.android.systemui.qs.panels.ui.compose.TileGrid
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
import com.android.systemui.scene.shared.model.Overlays
@@ -32,7 +50,6 @@
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
-import java.util.Optional
import javax.inject.Inject
@SysUISingleton
@@ -62,10 +79,10 @@
) {
val viewModel =
rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
+
OverlayShade(
modifier = modifier,
- viewModelFactory = viewModel.overlayShadeViewModelFactory,
- lockscreenContent = { Optional.empty() },
+ onScrimClicked = viewModel::onScrimClicked,
) {
Column {
ExpandedShadeHeader(
@@ -83,3 +100,61 @@
}
}
}
+
+@Composable
+fun ShadeBody(
+ viewModel: QuickSettingsContainerViewModel,
+) {
+ val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
+
+ AnimatedContent(
+ targetState = isEditing,
+ transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }
+ ) { editing ->
+ if (editing) {
+ EditMode(
+ viewModel = viewModel.editModeViewModel,
+ modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
+ )
+ } else {
+ QuickSettingsLayout(
+ viewModel = viewModel,
+ modifier = Modifier.sysuiResTag("quick_settings_panel")
+ )
+ }
+ }
+}
+
+@Composable
+private fun QuickSettingsLayout(
+ viewModel: QuickSettingsContainerViewModel,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+ ) {
+ BrightnessSliderContainer(
+ viewModel = viewModel.brightnessSliderViewModel,
+ modifier =
+ Modifier.fillMaxWidth()
+ .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+ )
+ TileGrid(
+ viewModel = viewModel.tileGridViewModel,
+ modifier =
+ Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+ viewModel.editModeViewModel::startEditing,
+ )
+ }
+}
+
+object QuickSettingsShade {
+
+ object Dimensions {
+ val Padding = 16.dp
+ val BrightnessSliderHeight = 64.dp
+ val GridMaxHeight = 800.dp
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 90d7da6..9316eb9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -16,51 +16,26 @@
package com.android.systemui.qs.ui.composable
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
-import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.qs.panels.ui.compose.EditMode
-import com.android.systemui.qs.panels.ui.compose.TileGrid
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutEnter
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutExit
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneActionsViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneContentViewModel
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
-import dagger.Lazy
-import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -70,12 +45,11 @@
constructor(
private val actionsViewModelFactory: QuickSettingsShadeSceneActionsViewModel.Factory,
private val contentViewModelFactory: QuickSettingsShadeSceneContentViewModel.Factory,
- private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.QuickSettingsShade
@@ -96,10 +70,10 @@
) {
val viewModel =
rememberViewModel("QuickSettingsShadeScene") { contentViewModelFactory.create() }
+
OverlayShade(
- viewModelFactory = viewModel.overlayShadeViewModelFactory,
- lockscreenContent = lockscreenContent,
modifier = modifier,
+ onScrimClicked = {},
) {
Column {
ExpandedShadeHeader(
@@ -117,68 +91,3 @@
}
}
}
-
-@Composable
-fun ShadeBody(
- viewModel: QuickSettingsContainerViewModel,
-) {
- val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
-
- AnimatedContent(
- targetState = isEditing,
- transitionSpec = { QuickSettingsLayoutEnter togetherWith QuickSettingsLayoutExit }
- ) { editing ->
- if (editing) {
- EditMode(
- viewModel = viewModel.editModeViewModel,
- modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
- )
- } else {
- QuickSettingsLayout(
- viewModel = viewModel,
- modifier = Modifier.sysuiResTag("quick_settings_panel")
- )
- }
- }
-}
-
-@Composable
-private fun QuickSettingsLayout(
- viewModel: QuickSettingsContainerViewModel,
- modifier: Modifier = Modifier,
-) {
- Column(
- verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
- ) {
- BrightnessSliderContainer(
- viewModel = viewModel.brightnessSliderViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
- )
- TileGrid(
- viewModel = viewModel.tileGridViewModel,
- modifier =
- Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
- viewModel.editModeViewModel::startEditing,
- )
- }
-}
-
-object QuickSettingsShade {
-
- object Dimensions {
- val Padding = 16.dp
- val BrightnessSliderHeight = 64.dp
- val GridMaxHeight = 800.dp
- }
-
- object Transitions {
- val QuickSettingsLayoutEnter: EnterTransition = fadeIn(tween(500))
- val QuickSettingsLayoutExit: ExitTransition = fadeOut(tween(500))
- val QuickSettingsEditorEnter: EnterTransition = fadeIn(tween(500))
- val QuickSettingsEditorExit: ExitTransition = fadeOut(tween(500))
- }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
deleted file mode 100644
index 3da6a02..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.ui.composable
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.scene.shared.model.Scene
-
-/** Compose-capable extension of [Scene]. */
-interface ComposableScene : Scene {
- @Composable fun SceneScope.Content(modifier: Modifier)
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index cbbace4..6fb4724 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -23,8 +23,8 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneDpAsState
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentDpAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.rememberViewModel
@@ -51,7 +51,7 @@
private val notificationStackScrolLView: Lazy<NotificationScrollView>,
private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
private val viewModelFactory: GoneSceneActionsViewModel.Factory,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Gone
private val actionsViewModel: GoneSceneActionsViewModel by lazy { viewModelFactory.create() }
@@ -67,11 +67,11 @@
override fun SceneScope.Content(
modifier: Modifier,
) {
- animateSceneFloatAsState(
+ animateContentFloatAsState(
value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
key = QuickSettings.SharedValues.TilesSquishiness,
)
- animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
+ animateContentDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
Spacer(modifier.fillMaxSize())
SnoozeableHeadsUpNotificationSpace(
stackScrollView = notificationStackScrolLView.get(),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
rename to packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
index 8e2e8a1..5319ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * 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.
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.scene.shared.model
+package com.android.systemui.scene.ui.composable
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.lifecycle.Activatable
@@ -56,4 +59,6 @@
* current scene is this one.
*/
val destinationScenes: Flow<Map<UserAction, UserActionResult>>
+
+ @Composable fun SceneScope.Content(modifier: Modifier)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index f9723d9..851fa3f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -54,11 +54,11 @@
* containers.
*
* @param viewModel The UI state holder for this container.
- * @param sceneByKey Mapping of [ComposableScene] by [SceneKey], ordered by z-order such that the
- * last scene is rendered on top of all other scenes. It's critical that this map contains exactly
- * and only the scenes on this container. In other words: (a) there should be no scene in this map
- * that is not in the configuration for this container and (b) all scenes in the configuration
- * must have entries in this map.
+ * @param sceneByKey Mapping of [Scene] by [SceneKey], ordered by z-order such that the last scene
+ * is rendered on top of all other scenes. It's critical that this map contains exactly and only
+ * the scenes on this container. In other words: (a) there should be no scene in this map that is
+ * not in the configuration for this container and (b) all scenes in the configuration must have
+ * entries in this map.
* @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the last
* overlay is rendered on top of all other overlays. It's critical that this map contains exactly
* and only the overlays on this container. In other words: (a) there should be no overlay in this
@@ -69,7 +69,7 @@
@Composable
fun SceneContainer(
viewModel: SceneContainerViewModel,
- sceneByKey: Map<SceneKey, ComposableScene>,
+ sceneByKey: Map<SceneKey, Scene>,
overlayByKey: Map<OverlayKey, Overlay>,
initialSceneKey: SceneKey,
dataSourceDelegator: SceneDataSourceDelegator,
@@ -123,16 +123,16 @@
},
) {
SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
- sceneByKey.forEach { (sceneKey, composableScene) ->
+ sceneByKey.forEach { (sceneKey, scene) ->
scene(
key = sceneKey,
userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap())
) {
// Activate the scene.
- LaunchedEffect(composableScene) { composableScene.activate() }
+ LaunchedEffect(scene) { scene.activate() }
// Render the scene.
- with(composableScene) {
+ with(scene) {
this@scene.Content(
modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 39fc7ef..a0ebca2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -1,13 +1,11 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.gestures.Orientation
-import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ProgressConverter
import com.android.compose.animation.scene.transitions
import com.android.systemui.bouncer.ui.composable.Bouncer
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
@@ -48,18 +46,8 @@
// Scene transitions
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
- from(Scenes.Gone, to = Scenes.NotificationsShade) {
- goneToNotificationsShadeTransition(Edge.Top)
- }
- from(Scenes.Gone, to = Scenes.NotificationsShade, key = OpenBottomShade) {
- goneToNotificationsShadeTransition(Edge.Bottom)
- }
- from(Scenes.Gone, to = Scenes.QuickSettingsShade) {
- goneToQuickSettingsShadeTransition(Edge.Top)
- }
- from(Scenes.Gone, to = Scenes.QuickSettingsShade, key = OpenBottomShade) {
- goneToQuickSettingsShadeTransition(Edge.Bottom)
- }
+ from(Scenes.Gone, to = Scenes.NotificationsShade) { goneToNotificationsShadeTransition() }
+ from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index e12a8bd..6738b97 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -69,7 +69,7 @@
state.setTargetScene(
targetScene = toScene,
transitionKey = transitionKey,
- coroutineScope = coroutineScope,
+ animationScope = coroutineScope,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
index fb41374..48ec198 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
@@ -16,12 +16,10 @@
package com.android.systemui.scene.ui.composable.transitions
-import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
fun TransitionBuilder.goneToNotificationsShadeTransition(
- edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
- toNotificationsShadeTransition(edge, durationScale)
+ toNotificationsShadeTransition(durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 05949b2..337f53a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -19,12 +19,9 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
@@ -32,11 +29,6 @@
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toNotificationsShadeTransition(
- /**
- * The edge where the shade will animate from. This is statically determined (i.e. doesn't
- * change during runtime).
- */
- edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
@@ -45,17 +37,11 @@
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return fromSceneSize.height.toFloat() * 2 / 3f
- }
- }
+ distance = UserActionDistance { fromSceneSize, orientation ->
+ fromSceneSize.height.toFloat() * 2 / 3f
+ }
- translate(OverlayShade.Elements.Panel, edge)
+ translate(OverlayShade.Elements.Panel, Edge.Top)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 595bbb0..8922224 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -40,56 +40,30 @@
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
-import com.android.systemui.util.kotlin.getOrNull
-import dagger.Lazy
-import java.util.Optional
-/** The overlay shade renders a lightweight shade UI container on top of a background scene. */
+/** Renders a lightweight shade UI container, as an overlay. */
@Composable
fun SceneScope.OverlayShade(
- viewModelFactory: OverlayShadeViewModel.Factory,
- lockscreenContent: Lazy<Optional<LockscreenContent>>,
+ onScrimClicked: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- val viewModel = rememberViewModel("OverlayShade") { viewModelFactory.create() }
- val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
-
Box(modifier) {
- if (backgroundScene == Scenes.Lockscreen) {
- // Lockscreen content is optionally injected, because variants of System UI without a
- // lockscreen cannot provide it.
- val lockscreenContentOrNull = lockscreenContent.get().getOrNull()
- lockscreenContentOrNull?.apply { Content(Modifier.fillMaxSize()) }
- }
-
- Scrim(onClicked = viewModel::onScrimClicked)
+ Scrim(onClicked = onScrimClicked)
Box(
modifier = Modifier.fillMaxSize().panelPadding(),
- contentAlignment =
- if (viewModel.panelAlignment == ShadeAlignment.Top) {
- Alignment.TopEnd
- } else {
- Alignment.BottomEnd
- },
+ contentAlignment = Alignment.TopEnd,
) {
Panel(
modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index d8ab0a1..5fcf522 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -106,7 +106,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.viewmodel.ShadeSceneActionsViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
@@ -120,6 +120,7 @@
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
object Shade {
@@ -146,6 +147,7 @@
}
/** The shade scene shows scrolling list of notifications and some of the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class ShadeScene
@Inject
@@ -161,7 +163,7 @@
private val mediaCarouselController: MediaCarouselController,
@Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
@Named(QS_PANEL) private val qsMediaHost: MediaHost,
-) : ExclusiveActivatable(), ComposableScene {
+) : ExclusiveActivatable(), Scene {
override val key = Scenes.Shade
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index 3a7c2bf..bd21a69 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -31,9 +31,6 @@
* layout or Compose drawing phases.
* 2. [ObservableTransitionState] values are backed by Kotlin [Flow]s and can be collected by
* non-Compose code to observe value changes.
- * 3. [ObservableTransitionState.Transition.fromScene] and
- * [ObservableTransitionState.Transition.toScene] will never be equal, while
- * [TransitionState.Transition.fromScene] and [TransitionState.Transition.toScene] can be equal.
*/
sealed interface ObservableTransitionState {
/**
@@ -54,9 +51,8 @@
/** There is a transition animating between two scenes. */
sealed class Transition(
- // TODO(b/353679003): Rename these to fromContent and toContent.
- open val fromScene: ContentKey,
- open val toScene: ContentKey,
+ val fromContent: ContentKey,
+ val toContent: ContentKey,
val currentOverlays: Flow<Set<OverlayKey>>,
val progress: Flow<Float>,
@@ -86,8 +82,8 @@
) : ObservableTransitionState {
override fun toString(): String =
"""Transition
- |(from=$fromScene,
- | to=$toScene,
+ |(from=$fromContent,
+ | to=$toContent,
| isInitiatedByUserInput=$isInitiatedByUserInput,
| isUserInputOngoing=$isUserInputOngoing
|)"""
@@ -95,8 +91,8 @@
/** A transition animating between [fromScene] and [toScene]. */
class ChangeScene(
- override val fromScene: SceneKey,
- override val toScene: SceneKey,
+ val fromScene: SceneKey,
+ val toScene: SceneKey,
val currentScene: Flow<SceneKey>,
currentOverlays: Flow<Set<OverlayKey>>,
progress: Flow<Float>,
@@ -196,8 +192,8 @@
fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
return this is Transition &&
- (from == null || this.fromScene == from) &&
- (to == null || this.toScene == to)
+ (from == null || this.fromContent == from) &&
+ (to == null || this.toContent == to)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index 3bf19fc..8480d3a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -18,14 +18,17 @@
import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
-import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.snap
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
-import kotlin.coroutines.cancellation.CancellationException
-import kotlinx.coroutines.coroutineScope
+import com.android.compose.animation.scene.UserActionResult.ChangeScene
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.transition.animateProgress
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.map
@Composable
internal fun PredictiveBackHandler(
@@ -34,19 +37,21 @@
) {
PredictiveBackHandler(
enabled = result != null,
- ) { progress: Flow<BackEventCompat> ->
+ ) { events: Flow<BackEventCompat> ->
if (result == null) {
// Note: We have to collect progress otherwise PredictiveBackHandler will throw.
- progress.first()
+ events.first()
return@PredictiveBackHandler
}
val animation =
createSwipeAnimation(
layoutImpl,
- result.userActionCopy(
- transitionKey = result.transitionKey ?: TransitionKey.PredictiveBack
- ),
+ if (result.transitionKey != null) {
+ result
+ } else {
+ result.copy(transitionKey = TransitionKey.PredictiveBack)
+ },
isUpOrLeft = false,
// Note that the orientation does not matter here given that it's only used to
// compute the distance. In our case the distance is always 1f.
@@ -54,47 +59,30 @@
distance = 1f,
)
- animate(layoutImpl, animation, progress)
+ animateProgress(
+ state = layoutImpl.state,
+ animation = animation,
+ progress = events.map { it.progress },
+
+ // Use the transformationSpec.progressSpec. We will lazily access it later once the
+ // transition has been started, because at this point the transformation spec of the
+ // transition is not computed yet.
+ commitSpec = null,
+
+ // The predictive back APIs will automatically animate the progress for us in this case
+ // so there is no need to animate it.
+ cancelSpec = snap(),
+ )
}
}
-private suspend fun <T : ContentKey> animate(
- layoutImpl: SceneTransitionLayoutImpl,
- animation: SwipeAnimation<T>,
- progress: Flow<BackEventCompat>,
-) {
- fun animateOffset(targetContent: T, spec: AnimationSpec<Float>? = null) {
- if (
- layoutImpl.state.transitionState != animation.contentTransition ||
- animation.isAnimatingOffset()
- ) {
- return
- }
-
- animation.animateOffset(
- initialVelocity = 0f,
- targetContent = targetContent,
- spec = spec,
- )
- }
-
- coroutineScope {
- launch {
- try {
- progress.collect { backEvent -> animation.dragOffset = backEvent.progress }
-
- // Back gesture successful.
- animateOffset(
- animation.toContent,
- animation.contentTransition.transformationSpec.progressSpec,
- )
- } catch (e: CancellationException) {
- // Back gesture cancelled.
- animateOffset(animation.fromContent)
- }
- }
-
- // Start the transition.
- layoutImpl.state.startTransition(animation.contentTransition)
+private fun UserActionResult.copy(
+ transitionKey: TransitionKey? = this.transitionKey
+): UserActionResult {
+ return when (this) {
+ is ChangeScene -> copy(transitionKey = transitionKey)
+ is ShowOverlay -> copy(transitionKey = transitionKey)
+ is HideOverlay -> copy(transitionKey = transitionKey)
+ is ReplaceByOverlay -> copy(transitionKey = transitionKey)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index a0d512c..094f20e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -492,17 +492,6 @@
) {
internal abstract fun toContent(currentScene: SceneKey): ContentKey
- internal fun userActionCopy(
- transitionKey: TransitionKey? = this.transitionKey
- ): UserActionResult {
- return when (this) {
- is ChangeScene -> copy(transitionKey = transitionKey)
- is ShowOverlay -> copy(transitionKey = transitionKey)
- is HideOverlay -> copy(transitionKey = transitionKey)
- is ReplaceByOverlay -> copy(transitionKey = transitionKey)
- }
- }
-
data class ChangeScene
internal constructor(
/** The scene we should be transitioning to during the [UserAction]. */
@@ -608,7 +597,7 @@
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
- val coroutineScope = rememberCoroutineScope()
+ val animationScope = rememberCoroutineScope()
val layoutImpl = remember {
SceneTransitionLayoutImpl(
state = state as MutableSceneTransitionLayoutStateImpl,
@@ -617,7 +606,7 @@
swipeSourceDetector = swipeSourceDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = builder,
- animationScope = coroutineScope,
+ animationScope = animationScope,
)
.also { onLayoutImpl?.invoke(it) }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index cc7d146..c2d5dd05 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -121,14 +121,13 @@
* might still be interrupted, for instance by another call to [setTargetScene] or by a user
* gesture.
*
- * If [coroutineScope] is cancelled during the transition and that the transition was still
+ * If [animationScope] is cancelled during the transition and that the transition was still
* active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be set to
* `TransitionState.Idle(targetScene)`.
*/
fun setTargetScene(
targetScene: SceneKey,
- // TODO(b/362727477): Rename to animationScope.
- coroutineScope: CoroutineScope,
+ animationScope: CoroutineScope,
transitionKey: TransitionKey? = null,
): Pair<TransitionState.Transition, Job>?
@@ -302,12 +301,12 @@
override fun setTargetScene(
targetScene: SceneKey,
- coroutineScope: CoroutineScope,
+ animationScope: CoroutineScope,
transitionKey: TransitionKey?,
): Pair<TransitionState.Transition.ChangeScene, Job>? {
checkThread()
- return coroutineScope.animateToScene(
+ return animationScope.animateToScene(
layoutState = this@MutableSceneTransitionLayoutStateImpl,
target = targetScene,
transitionKey = transitionKey,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index bd4627d..2a09a77 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -218,8 +218,8 @@
val animatable = offsetAnimation
val offset =
when {
+ isInPreviewStage -> 0f
animatable != null -> animatable.value
- contentTransition.previewTransformationSpec != null -> 0f
else -> dragOffset
}
@@ -247,7 +247,15 @@
}
val previewProgress: Float
- get() = computeProgress(dragOffset)
+ get() {
+ val offset =
+ if (isInPreviewStage) {
+ offsetAnimation?.value ?: dragOffset
+ } else {
+ dragOffset
+ }
+ return computeProgress(offset)
+ }
val previewProgressVelocity: Float
get() = 0f
@@ -348,7 +356,11 @@
}
val startProgress =
- if (contentTransition.previewTransformationSpec != null) 0f else dragOffset
+ if (contentTransition.previewTransformationSpec != null && targetContent == toContent) {
+ 0f
+ } else {
+ dragOffset
+ }
val animatable =
Animatable(startProgress, OffsetVisibilityThreshold).also { offsetAnimation = it }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 2b5953c..1f82e0b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -104,23 +104,23 @@
): TransitionSpec
/**
- * Define the animation to be played when the [scene] is overscrolled in the given
+ * Define the animation to be played when the [content] is overscrolled in the given
* [orientation].
*
* The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
* [distance] down/right, -1f when moving in the opposite direction.
*/
fun overscroll(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit,
): OverscrollSpec
/**
- * Prevents overscroll the [scene] in the given [orientation], allowing ancestors to eventually
- * consume the remaining gesture.
+ * Prevents overscroll the [content] in the given [orientation], allowing ancestors to
+ * eventually consume the remaining gesture.
*/
- fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec
+ fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec
}
interface BaseTransitionBuilder : PropertyTransformationBuilder {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 18e356f..da4c8d8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -84,30 +84,30 @@
}
override fun overscroll(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit
): OverscrollSpec {
val impl = OverscrollBuilderImpl().apply(builder)
check(impl.transformations.isNotEmpty()) {
"This method does not allow empty transformations. " +
- "Use overscrollDisabled($scene, $orientation) instead."
+ "Use overscrollDisabled($content, $orientation) instead."
}
- return overscrollSpec(scene, orientation, impl)
+ return overscrollSpec(content, orientation, impl)
}
- override fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec {
- return overscrollSpec(scene, orientation, OverscrollBuilderImpl())
+ override fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec {
+ return overscrollSpec(content, orientation, OverscrollBuilderImpl())
}
private fun overscrollSpec(
- scene: SceneKey,
+ content: ContentKey,
orientation: Orientation,
impl: OverscrollBuilderImpl,
): OverscrollSpec {
val spec =
OverscrollSpecImpl(
- content = scene,
+ content = content,
orientation = orientation,
transformationSpec =
TransformationSpecImpl(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
new file mode 100644
index 0000000..715d979
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.compose.animation.scene.transition
+
+import androidx.annotation.FloatRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.util.fastCoerceIn
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SwipeAnimation
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.createSwipeAnimation
+import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+/**
+ * Seek to the given [scene] using [progress].
+ *
+ * This will start a transition from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene] to [scene], driven by the
+ * progress in [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToScene(
+ scene: SceneKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(scene != currentScene) {
+ "seekToScene($scene) has to be called with a different scene than the current scene"
+ }
+
+ seek(UserActionResult.ChangeScene(scene, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to show the given [overlay] using [progress].
+ *
+ * This will start a transition to show [overlay] from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToShowOverlay(
+ overlay: OverlayKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(overlay in currentOverlays) {
+ "seekToShowOverlay($overlay) can be called only when the overlay is in currentOverlays"
+ }
+
+ seek(UserActionResult.ShowOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to hide the given [overlay] using [progress].
+ *
+ * This will start a transition to hide [overlay] to the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToHideOverlay(
+ overlay: OverlayKey,
+ @FloatRange(0.0, 1.0) progress: Flow<Float>,
+ transitionKey: TransitionKey? = null,
+ animationSpec: AnimationSpec<Float>? = null,
+) {
+ require(overlay !in currentOverlays) {
+ "seekToHideOverlay($overlay) can be called only when the overlay is *not* in " +
+ "currentOverlays"
+ }
+
+ seek(UserActionResult.HideOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+private suspend fun MutableSceneTransitionLayoutState.seek(
+ result: UserActionResult,
+ progress: Flow<Float>,
+ animationSpec: AnimationSpec<Float>?,
+) {
+ val layoutState =
+ when (this) {
+ is MutableSceneTransitionLayoutStateImpl -> this
+ }
+
+ val swipeAnimation =
+ createSwipeAnimation(
+ layoutState = layoutState,
+ result = result,
+
+ // We are animating progress, so distance is always 1f.
+ distance = 1f,
+
+ // The orientation and isUpOrLeft don't matter here given that they are only used during
+ // overscroll, which is disabled for progress-based transitions.
+ orientation = Orientation.Horizontal,
+ isUpOrLeft = false,
+ )
+
+ animateProgress(
+ state = layoutState,
+ animation = swipeAnimation,
+ progress = progress,
+ commitSpec = animationSpec,
+ cancelSpec = animationSpec,
+ )
+}
+
+internal suspend fun <T : ContentKey> animateProgress(
+ state: MutableSceneTransitionLayoutStateImpl,
+ animation: SwipeAnimation<T>,
+ progress: Flow<Float>,
+ commitSpec: AnimationSpec<Float>?,
+ cancelSpec: AnimationSpec<Float>?,
+) {
+ fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
+ if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
+ return
+ }
+
+ animation.animateOffset(
+ initialVelocity = 0f,
+ targetContent = targetContent,
+
+ // Important: we have to specify a spec that correctly animates *progress* (low
+ // visibility threshold) and not *offset* (higher visibility threshold).
+ spec = spec ?: animation.contentTransition.transformationSpec.progressSpec,
+ )
+ }
+
+ coroutineScope {
+ val collectionJob = launch {
+ try {
+ progress.collectLatest { progress ->
+ // Progress based animation should never overscroll given that the
+ // absoluteDistance exposed to overscroll builders is always 1f and will not
+ // lead to any noticeable transformation.
+ animation.dragOffset = progress.fastCoerceIn(0f, 1f)
+ }
+
+ // Transition committed.
+ animateOffset(animation.toContent, commitSpec)
+ } catch (e: CancellationException) {
+ // Transition cancelled.
+ animateOffset(animation.fromContent, cancelSpec)
+ }
+ }
+
+ // Start the transition.
+ state.startTransition(animation.contentTransition)
+
+ // The transition is done. Cancel the collection in case the transition was finished because
+ // it was interrupted by another transition.
+ if (collectionJob.isActive) {
+ collectionJob.cancel()
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 26743fc..79f82c9 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -200,6 +200,8 @@
fromScene: SceneKey? = null,
toScene: SceneKey? = null,
progress: Float? = null,
+ previewProgress: Float? = null,
+ isInPreviewStage: Boolean? = null,
isUserInputOngoing: Boolean? = null
): Transition {
val transition = assertThat(transitionState).isSceneTransition()
@@ -207,6 +209,10 @@
fromScene?.let { assertThat(transition).hasFromScene(it) }
toScene?.let { assertThat(transition).hasToScene(it) }
progress?.let { assertThat(transition).hasProgress(it) }
+ previewProgress?.let { assertThat(transition).hasPreviewProgress(it) }
+ isInPreviewStage?.let {
+ assertThat(transition).run { if (it) isInPreviewStage() else isNotInPreviewStage() }
+ }
isUserInputOngoing?.let { assertThat(transition).hasIsUserInputOngoing(it) }
return transition
}
@@ -353,6 +359,32 @@
}
@Test
+ fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene_previewAnimated() =
+ runGestureTest {
+ layoutState.transitions = transitions {
+ // set a preview for the transition
+ from(SceneA, to = SceneC, preview = {}) {}
+ }
+ val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
+ assertTransition(currentScene = SceneA)
+
+ dragController.onDragStopped(velocity = velocityThreshold - 0.01f)
+ runCurrent()
+
+ // verify that transition remains in preview stage and animates back to fromScene
+ assertTransition(
+ currentScene = SceneA,
+ isInPreviewStage = true,
+ previewProgress = 0.1f,
+ progress = 0f
+ )
+
+ // wait for the stop animation
+ advanceUntilIdle()
+ assertIdle(currentScene = SceneA)
+ }
+
+ @Test
fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
assertTransition(currentScene = SceneA)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 7498df1..bc929bd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -44,8 +44,8 @@
transitions { /* default interruption handler */ },
)
- state.setTargetScene(SceneB, coroutineScope = this)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
@@ -81,8 +81,8 @@
},
)
- state.setTargetScene(SceneB, coroutineScope = this)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
@@ -124,10 +124,10 @@
// Animate to B and advance the transition a little bit so that progress > visibility
// threshold and that reversing from B back to A won't immediately snap to A.
- state.setTargetScene(SceneB, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
testScheduler.advanceTimeBy(duration / 2L)
- state.setTargetScene(SceneC, coroutineScope = this)
+ state.setTargetScene(SceneC, animationScope = this)
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
@@ -166,7 +166,7 @@
SceneA,
// We use testScope here and not backgroundScope because setTargetScene
// needs the monotonic clock that is only available in the test scope.
- coroutineScope = this,
+ animationScope = this,
)
)
.first
@@ -200,7 +200,7 @@
SceneB,
// We use testScope here and not backgroundScope because setTargetScene
// needs the monotonic clock that is only available in the test scope.
- coroutineScope = this,
+ animationScope = this,
)
)
.first
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index 2c723ec..f3161f3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -90,16 +90,16 @@
at(0) {
val state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene)
+ assertThat((state as ObservableTransitionState.Transition).fromContent)
.isEqualTo(SceneA)
- assertThat(state.toScene).isEqualTo(SceneB)
+ assertThat(state.toContent).isEqualTo(SceneB)
assertThat(state.progress()).isEqualTo(0f)
}
at(TestTransitionDuration / 2) {
val state = observableState()
- assertThat((state as ObservableTransitionState.Transition).fromScene)
+ assertThat((state as ObservableTransitionState.Transition).fromContent)
.isEqualTo(SceneA)
- assertThat(state.toScene).isEqualTo(SceneB)
+ assertThat(state.toContent).isEqualTo(SceneB)
assertThat(state.progress()).isEqualTo(0.5f)
}
after {
@@ -200,7 +200,7 @@
var state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene).isEqualTo(SceneA)
+ assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
assertThat(state.previewProgress()).isEqualTo(0.4f)
assertThat(state.isInPreviewStage()).isEqualTo(true)
@@ -218,7 +218,7 @@
}
state = observableState()
assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
- assertThat((state as ObservableTransitionState.Transition).fromScene).isEqualTo(SceneA)
+ assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
assertThat(state.previewProgress()).isEqualTo(0.4f)
assertThat(state.isInPreviewStage()).isEqualTo(false)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 29eedf6..cd20a29a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -30,13 +30,17 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.subjects.assertThat
import com.android.compose.animation.scene.transition.link.StateLink
+import com.android.compose.animation.scene.transition.seekToScene
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.TestTransition
import com.android.compose.test.runMonotonicClockTest
import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Rule
@@ -76,13 +80,13 @@
@Test
fun setTargetScene_idleToSameScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
+ assertThat(state.setTargetScene(SceneA, animationScope = this)).isNull()
}
@Test
fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- val (transition, job) = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this))
+ val (transition, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
assertThat(state.transitionState).isEqualTo(transition)
job.join()
@@ -93,8 +97,8 @@
fun setTargetScene_transitionToSameScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- val (_, job) = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this))
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNull()
+ val (_, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNull()
job.join()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
@@ -104,8 +108,8 @@
fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutState(SceneA)
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
- val (_, job) = checkNotNull(state.setTargetScene(SceneC, coroutineScope = this))
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
+ val (_, job) = checkNotNull(state.setTargetScene(SceneC, animationScope = this))
job.join()
assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
@@ -118,7 +122,7 @@
lateinit var transition: TransitionState.Transition
val job =
launch(start = CoroutineStart.UNDISPATCHED) {
- transition = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this)).first
+ transition = checkNotNull(state.setTargetScene(SceneB, animationScope = this)).first
}
assertThat(state.transitionState).isEqualTo(transition)
@@ -285,11 +289,11 @@
)
// Default transition from A to B.
- assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
+ assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
// Go back to A.
- state.setTargetScene(SceneA, coroutineScope = this)
+ state.setTargetScene(SceneA, animationScope = this)
testScheduler.advanceUntilIdle()
assertThat(state.transitionState).isIdle()
assertThat(state.transitionState).hasCurrentScene(SceneA)
@@ -298,7 +302,7 @@
assertThat(
state.setTargetScene(
SceneB,
- coroutineScope = this,
+ animationScope = this,
transitionKey = transitionkey,
)
)
@@ -634,7 +638,7 @@
val state = MutableSceneTransitionLayoutState(SceneA)
// Transition to B.
- state.setTargetScene(SceneB, coroutineScope = this)
+ state.setTargetScene(SceneB, animationScope = this)
val transition = assertThat(state.transitionState).isSceneTransition()
assertThat(transition).hasCurrentScene(SceneB)
@@ -660,4 +664,75 @@
assertThat(state.transitionState).isIdle()
assertThat(state.transitionState).hasCurrentScene(SceneC)
}
+
+ @Test
+ fun seekToScene() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ assertThat(transition).hasProgress(0f)
+
+ // Change progress.
+ progress.send(0.4f)
+ assertThat(transition).hasProgress(0.4f)
+
+ // Close the channel normally to confirm the transition.
+ progress.close()
+ job.join()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneB)
+ }
+
+ @Test
+ fun seekToScene_cancelled() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ val transition = assertThat(state.transitionState).isSceneTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ assertThat(transition).hasProgress(0f)
+
+ // Change progress.
+ progress.send(0.4f)
+ assertThat(transition).hasProgress(0.4f)
+
+ // Close the channel with a CancellationException to cancel the transition.
+ progress.close(CancellationException())
+ job.join()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneA)
+ }
+
+ @Test
+ fun seekToScene_interrupted() = runMonotonicClockTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+ val progress = Channel<Float>()
+
+ val job =
+ launch(start = CoroutineStart.UNDISPATCHED) {
+ state.seekToScene(SceneB, progress.consumeAsFlow())
+ }
+
+ assertThat(state.transitionState).isSceneTransition()
+
+ // Start a new transition, interrupting the seek transition.
+ state.setTargetScene(SceneB, animationScope = this)
+
+ // The previous job is cancelled and does not infinitely collect the progress.
+ job.join()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index c5a5173..25f9564 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -246,7 +246,7 @@
content = { play ->
LaunchedEffect(play) {
if (play) {
- state.setTargetScene(toScene, coroutineScope = this)
+ state.setTargetScene(toScene, animationScope = this)
}
}
@@ -284,7 +284,7 @@
testTransition(
state = state,
- changeState = { state -> state.setTargetScene(to, coroutineScope = this) },
+ changeState = { state -> state.setTargetScene(to, animationScope = this) },
transitionLayout = transitionLayout,
builder = builder,
)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index 362e23d..96d79df 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -71,7 +71,7 @@
.stateIn(
scope = backgroundScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ initialValue = true,
)
/** The default duration for DND mode when enabled. See [Settings.Secure.ZEN_DURATION]. */
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 2f50bbd..a7740c6 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -121,7 +121,7 @@
do so by defining their own scene. This section describes how to do that.
Each scene is defined as an implementation of the
-[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt)
+[`Scene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt)
interface, which has three parts: 1. The `key` property returns the
[`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)
that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns
@@ -138,7 +138,7 @@
For example:
```kotlin
-@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : ComposableScene {
+@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : Scene {
override val key = SceneKey.YourScene
override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 5f6ea1c..062d351 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -84,6 +84,7 @@
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock private lateinit var postureController: DevicePostureController
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor private lateinit var keyListenerArgumentCaptor: ArgumentCaptor<View.OnKeyListener>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -132,6 +133,7 @@
mSelectedUserInteractor,
keyguardKeyboardInteractor,
null,
+ mUserActivityNotifier
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 2af3b00..1076d90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -87,6 +87,8 @@
private View mOkButton;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private UserActivityNotifier mUserActivityNotifier;
private NumPadKey[] mButtons = new NumPadKey[]{};
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -117,7 +119,7 @@
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
mEmergencyButtonController, mFalsingCollector, featureFlags,
- mSelectedUserInteractor, keyguardKeyboardInteractor, null) {
+ mSelectedUserInteractor, keyguardKeyboardInteractor, null, mUserActivityNotifier) {
@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 fabc357..18ed22a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -154,6 +154,7 @@
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var postureController: DevicePostureController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor
private lateinit var swipeListenerArgumentCaptor:
@@ -240,6 +241,7 @@
mSelectedUserInteractor,
keyguardKeyboardInteractor,
null,
+ mUserActivityNotifier
)
kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
index 460461a..176c3ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
@@ -22,10 +22,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@@ -38,13 +39,15 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
+
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
// mocks
@Mock private lateinit var a11yManager: AccessibilityManager
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
- private val secureSettings = FakeSettings()
private val userA11yQsShortcutsRepositoryFactory =
object : UserA11yQsShortcutsRepository.Factory {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
index 4e1f82c..801d359 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,9 +42,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private lateinit var underTest: ColorCorrectionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
index b99dec4..2f457be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -41,9 +42,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private lateinit var underTest: ColorInversionRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
index 5757f67..54dbed8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
@@ -26,7 +26,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -38,8 +40,6 @@
import com.google.common.truth.Truth.assertThat
import java.time.LocalTime
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -51,7 +51,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayRepositoryTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = testKosmos()
private val testUser = UserHandle.of(1)!!
private val testStartTime = LocalTime.MIDNIGHT
private val testEndTime = LocalTime.NOON
@@ -71,8 +71,8 @@
}
private val globalSettings = kosmos.fakeGlobalSettings
private val secureSettings = kosmos.fakeSettings
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
private val userScopedColorDisplayManager =
mock<UserScopedService<ColorDisplayManager>> {
whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
index 1378dac..729d356 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
@@ -22,11 +22,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -39,9 +40,10 @@
private val testUser1 = UserHandle.of(1)!!
private val testUser2 = UserHandle.of(2)!!
- private val testDispatcher = StandardTestDispatcher()
- private val scope = TestScope(testDispatcher)
- private val settings: FakeSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
private val underTest: OneHandedModeRepository =
OneHandedModeRepositoryImpl(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
index ce22e28..62f13f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
@@ -21,10 +21,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,9 +33,10 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class UserA11yQsShortcutsRepositoryTest : SysuiTestCase() {
- private val secureSettings = FakeSettings()
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
private val underTest =
UserA11yQsShortcutsRepository(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
index c37b33e..ae1c496 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
@@ -29,7 +29,7 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,8 +45,7 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
-
- private lateinit var secureSettings: FakeSettings
+ private val secureSettings = kosmos.fakeSettings
private lateinit var userRepository: FakeUserRepository
private lateinit var underTest: CommunalTutorialRepositoryImpl
@@ -55,7 +54,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- secureSettings = FakeSettings()
userRepository = FakeUserRepository()
val listOfUserInfo = listOf(MAIN_USER_INFO)
userRepository.setUserInfos(listOfUserInfo)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index bbfaf6f..6c3c7ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -31,19 +31,19 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -63,27 +63,24 @@
@RunWith(AndroidJUnit4::class)
class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var zenModeController: ZenModeController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var conditionUri: Uri
@Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
@Captor private lateinit var spyZenMode: ArgumentCaptor<Int>
@Captor private lateinit var spyConditionId: ArgumentCaptor<Uri?>
- private lateinit var settings: FakeSettings
private lateinit var underTest: DoNotDisturbQuickAffordanceConfig
- private lateinit var testDispatcher: TestDispatcher
- private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
-
- settings = FakeSettings()
-
underTest =
DoNotDisturbQuickAffordanceConfig(
context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb23..0145f17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,19 +23,19 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -51,14 +51,15 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val settings = kosmos.unconfinedDispatcherFakeSettings
+
@Mock private lateinit var sharedPrefs: FakeSharedPreferences
private lateinit var underTest: KeyguardQuickAffordanceLegacySettingSyncer
-
- private lateinit var testScope: TestScope
- private lateinit var testDispatcher: TestDispatcher
private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
- private lateinit var settings: FakeSettings
@Before
fun setUp() {
@@ -73,8 +74,6 @@
whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true)
whenever(context.resources).thenReturn(resources)
- testDispatcher = UnconfinedTestDispatcher()
- testScope = TestScope(testDispatcher)
selectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -92,7 +91,6 @@
userTracker = FakeUserTracker(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
- settings = FakeSettings()
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET, 0)
settings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index c85cd66..1582e47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -31,20 +31,21 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -58,6 +59,11 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
private lateinit var underTest: KeyguardQuickAffordanceRepository
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
@@ -65,7 +71,6 @@
private lateinit var userTracker: FakeUserTracker
private lateinit var client1: FakeCustomizationProviderClient
private lateinit var client2: FakeCustomizationProviderClient
- private lateinit var testScope: TestScope
@Before
fun setUp() {
@@ -73,8 +78,6 @@
context.resources.configuration.setLayoutDirection(Locale.US)
config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
userTracker = FakeUserTracker()
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -128,7 +131,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs = setOf(config1, config2),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index ad07c1c..a8bb2b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -61,7 +61,7 @@
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -80,6 +80,10 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@@ -90,11 +94,8 @@
@Mock private lateinit var logger: KeyguardQuickAffordancesLogger
@Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
- private val kosmos = testKosmos()
-
private lateinit var underTest: KeyguardQuickAffordanceInteractor
- private val testScope = kosmos.testScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
@@ -170,7 +171,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = kosmos.testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index cdba4db..f6865f13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayActionsViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -44,15 +43,13 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
- private val underTest by lazy { kosmos.notificationsShadeOverlayActionsViewModel }
+ private val underTest = kosmos.notificationsShadeOverlayActionsViewModel
@Test
- fun upTransitionSceneKey_topAligned_hidesShade() =
+ fun upTransitionSceneKey_hidesShade() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
- fakeShadeRepository.setDualShadeAlignedToBottom(false)
underTest.activateIn(this)
assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
@@ -61,18 +58,6 @@
}
@Test
- fun upTransitionSceneKey_bottomAligned_doesNothing() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
-
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.HideOverlay)?.overlay)
- .isEqualTo(Overlays.NotificationsShade)
- }
-
- @Test
fun back_hidesShade() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..88a1df1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -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.systemui.notifications.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+@EnableSceneContainer
+class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ private val underTest = kosmos.notificationsShadeOverlayContentViewModel
+
+ @Test
+ fun onScrimClicked_hidesShade() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ sceneInteractor.showOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = "test",
+ )
+ assertThat(currentOverlays).contains(Overlays.NotificationsShade)
+
+ underTest.onScrimClicked()
+
+ assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
index 0505e19..ed7f96fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
@@ -37,7 +37,6 @@
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneActionsViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -104,36 +103,6 @@
}
@Test
- fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
- .isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- val actions by collectLastValue(underTest.actions)
- lockDevice()
- unlockDevice()
- underTest.activateIn(this)
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
@@ -153,11 +122,13 @@
@Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
+ val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
val actions by collectLastValue(underTest.actions)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.None
)
+ assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
sceneInteractor // force the lazy; this will kick off StateFlows
runCurrent()
sceneInteractor.changeScene(Scenes.Gone, "reason")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index d153e9d..5619022 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -21,14 +21,15 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,10 +39,11 @@
@RunWith(AndroidJUnit4::class)
class AutoAddableSettingTest : SysuiTestCase() {
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
- private val secureSettings = FakeSettings()
private val underTest =
AutoAddableSetting(
secureSettings,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index fbfefb9..762941d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -43,15 +42,13 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
- private val underTest by lazy { kosmos.quickSettingsShadeOverlayActionsViewModel }
+ private val underTest = kosmos.quickSettingsShadeOverlayActionsViewModel
@Test
- fun upTransitionSceneKey_topAligned_hidesShade() =
+ fun upTransitionSceneKey_hidesShade() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
- fakeShadeRepository.setDualShadeAlignedToBottom(false)
underTest.activateIn(this)
assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
@@ -60,18 +57,6 @@
}
@Test
- fun upTransitionSceneKey_bottomAligned_doesNothing() =
- testScope.runTest {
- val actions by collectLastValue(underTest.actions)
- fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
-
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.HideOverlay)?.overlay)
- .isEqualTo(Overlays.QuickSettingsShade)
- }
-
- @Test
fun back_hidesShade() =
testScope.runTest {
val actions by collectLastValue(underTest.actions)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..abd1e2c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+@EnableSceneContainer
+class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val sceneInteractor = kosmos.sceneInteractor
+
+ private val underTest = kosmos.quickSettingsShadeOverlayContentViewModel
+
+ @Test
+ fun onScrimClicked_hidesShade() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ sceneInteractor.showOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = "test",
+ )
+ assertThat(currentOverlays).contains(Overlays.QuickSettingsShade)
+
+ underTest.onScrimClicked()
+
+ assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
index db58c85..ba527d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
@@ -39,7 +39,6 @@
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -107,37 +106,6 @@
}
@Test
- fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
- testScope.runTest {
- kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
- underTest.activateIn(this)
- val actions by collectLastValue(underTest.actions)
- val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
- lockDevice()
- unlockDevice()
-
- assertThat((actions?.get(Swipe.Down) as? UserActionResult.ChangeScene)?.toScene)
- .isEqualTo(SceneFamilies.Home)
- assertThat(actions?.get(Swipe.Up)).isNull()
- assertThat(homeScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
underTest.activateIn(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
deleted file mode 100644
index 3f087b4..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
+++ /dev/null
@@ -1,170 +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.shade.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-@EnableSceneContainer
-class OverlayShadeViewModelTest : SysuiTestCase() {
-
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val sceneInteractor = kosmos.sceneInteractor
- private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
- private val underTest = kosmos.overlayShadeViewModel
-
- @Before
- fun setUp() {
- underTest.activateIn(testScope)
- }
-
- @Test
- fun backgroundScene_deviceLocked_lockscreen() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun backgroundScene_deviceUnlocked_gone() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
- unlockDevice()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun backgroundScene_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun backgroundScene_authMethodSwipe_lockscreenDismissed_goesToGone() =
- testScope.runTest {
- val backgroundScene by collectLastValue(underTest.backgroundScene)
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
-
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- }
-
- @Test
- fun onScrimClicked_onLockscreen_goesToLockscreen() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- lockDevice()
- sceneInteractor.changeScene(Scenes.Bouncer, "reason")
- runCurrent()
- assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
-
- underTest.onScrimClicked()
-
- assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
- }
-
- @Test
- fun onScrimClicked_deviceWasEntered_goesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene)
- val backgroundScene by collectLastValue(underTest.backgroundScene)
-
- lockDevice()
- unlockDevice()
- sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
- runCurrent()
- assertThat(backgroundScene).isEqualTo(Scenes.Gone)
- assertThat(currentScene).isNotEqualTo(Scenes.Gone)
-
- underTest.onScrimClicked()
-
- assertThat(currentScene).isEqualTo(Scenes.Gone)
- }
-
- private fun TestScope.lockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
- sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
- runCurrent()
- }
-
- private fun TestScope.unlockDevice() {
- val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
- sceneInteractor.changeScene(Scenes.Gone, "reason")
- runCurrent()
- }
-}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e8fd2ef..38ef0e9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1052,9 +1052,6 @@
<!-- The width of the shortcut helper container, as a fraction of the screen's width. -->
<item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">1.0</item>
- <!-- Only applicable for dual shade - Allow Notifications/QS shade to anchor to the bottom. -->
- <bool name="config_dualShadeAlignedToBottom">false</bool>
-
<!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
<string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 64ccbe1..28f1381 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -20,6 +20,7 @@
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 static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
@@ -55,6 +56,7 @@
private final LatencyTracker mLatencyTracker;
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
+ private final UserActivityNotifier mUserActivityNotifier;
private CountDownTimer mCountdownTimer;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
@@ -89,7 +91,8 @@
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -98,6 +101,7 @@
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
mMSDLPlayer = msdlPlayer;
+ mUserActivityNotifier = userActivityNotifier;
}
abstract void resetState();
@@ -303,6 +307,9 @@
getKeyguardSecurityCallback().userActivity();
getKeyguardSecurityCallback().onUserInput();
mMessageAreaController.setMessage("");
+ if (notifyPasswordTextViewUserActivityInBackground()) {
+ mUserActivityNotifier.notifyUserActivity();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 45fdbc6..dd84bc6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -220,6 +220,7 @@
private final UiEventLogger mUiEventLogger;
private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
private final MSDLPlayer mMSDLPlayer;
+ private final UserActivityNotifier mUserActivityNotifier;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -235,7 +236,8 @@
FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
UiEventLogger uiEventLogger,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- MSDLPlayer msdlPlayer) {
+ MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -254,6 +256,7 @@
mUiEventLogger = uiEventLogger;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
mMSDLPlayer = msdlPlayer;
+ mUserActivityNotifier = userActivityNotifier;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -276,29 +279,29 @@
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor, mMSDLPlayer);
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
- mUiEventLogger, mKeyguardKeyboardInteractor, mMSDLPlayer
- );
+ mUiEventLogger, mKeyguardKeyboardInteractor, mMSDLPlayer,
+ mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor, mMSDLPlayer);
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
- mKeyguardKeyboardInteractor, mMSDLPlayer
+ mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier
);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 6983a06..905fa09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -138,10 +138,12 @@
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+ userActivityNotifier);
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 dd7c3e4..f575cf2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -83,10 +83,12 @@
FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer);
+ emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+ userActivityNotifier);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 7fc038f..3b5bf1a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -65,11 +65,12 @@
DevicePostureController postureController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor, msdlPlayer);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index ce5b5d7..47fe2b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -99,11 +99,12 @@
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor, msdlPlayer);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
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 86b29b2..c688acb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -96,11 +96,12 @@
EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
SelectedUserInteractor selectedUserInteractor,
KeyguardKeyboardInteractor keyguardKeyboardInteractor,
- @Nullable MSDLPlayer msdlPlayer) {
+ @Nullable MSDLPlayer msdlPlayer,
+ UserActivityNotifier userActivityNotifier) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
- keyguardKeyboardInteractor, msdlPlayer);
+ keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 85f8b48..0c4bc0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -257,7 +259,9 @@
@Override
protected void onUserActivity() {
- mPM.userActivity(SystemClock.uptimeMillis(), false);
+ if (!notifyPasswordTextViewUserActivityInBackground()) {
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
super.onUserActivity();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt
new file mode 100644
index 0000000..9b1ddb7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.PowerManager
+import android.os.SystemClock
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/** Wrapper class for notifying the system about user activity in the background. */
+class UserActivityNotifier
+@Inject
+constructor(
+ @UiBackground private val uiBgExecutor: Executor,
+ private val powerManager: PowerManager
+) {
+
+ fun notifyUserActivity() {
+ uiBgExecutor.execute {
+ powerManager.userActivity(
+ SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 578389b..13f6bba 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -23,35 +23,25 @@
import androidx.annotation.DimenRes
import androidx.annotation.LayoutRes
import com.android.settingslib.Utils
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
import com.android.systemui.statusbar.policy.onThemeChanged
import com.android.systemui.util.kotlin.emitOnStart
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-/** Configuration-aware-state-tracking utilities. */
-class ConfigurationState
-@Inject
-constructor(
- private val configurationController: ConfigurationController,
- @Application private val context: Context,
- private val layoutInflater: LayoutInflater,
-) {
+interface ConfigurationState {
/**
* Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
* configuration.
*
* @see android.content.res.Resources.getDimensionPixelSize
*/
- fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
- return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
- context.resources.getDimensionPixelSize(id)
- }
- }
+ fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int>
/**
* Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
@@ -59,22 +49,14 @@
*
* @see android.content.res.Resources.getDimensionPixelSize
*/
- fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
- return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
- context.resources.getDimensionPixelOffset(id)
- }
- }
+ fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int>
/**
* Returns a [Flow] that emits a color that is kept in sync with the device theme.
*
* @see Utils.getColorAttrDefaultColor
*/
- fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
- return configurationController.onThemeChanged.emitOnStart().map {
- Utils.getColorAttrDefaultColor(context, id, defaultValue)
- }
- }
+ fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int>
/**
* Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
@@ -87,6 +69,65 @@
@LayoutRes id: Int,
root: ViewGroup?,
attachToRoot: Boolean,
+ ): Flow<T>
+}
+
+/** Configuration-aware-state-tracking utilities. */
+class ConfigurationStateImpl
+@AssistedInject
+constructor(
+ @Assisted private val configurationController: ConfigurationController,
+ @Assisted private val context: Context,
+) : ConfigurationState {
+
+ private val layoutInflater = LayoutInflater.from(context)
+
+ /**
+ * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+ * configuration.
+ *
+ * @see android.content.res.Resources.getDimensionPixelSize
+ */
+ override fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
+ return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+ context.resources.getDimensionPixelSize(id)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+ * configuration.
+ *
+ * @see android.content.res.Resources.getDimensionPixelSize
+ */
+ override fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
+ return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+ context.resources.getDimensionPixelOffset(id)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a color that is kept in sync with the device theme.
+ *
+ * @see Utils.getColorAttrDefaultColor
+ */
+ override fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
+ return configurationController.onThemeChanged.emitOnStart().map {
+ Utils.getColorAttrDefaultColor(context, id, defaultValue)
+ }
+ }
+
+ /**
+ * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
+ * the device configuration.
+ *
+ * @see LayoutInflater.inflate
+ */
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : View> inflateLayout(
+ @LayoutRes id: Int,
+ root: ViewGroup?,
+ attachToRoot: Boolean,
): Flow<T> {
// TODO(b/305930747): This may lead to duplicate invocations if both flows emit, find a
// solution to only emit one event.
@@ -97,4 +138,16 @@
.emitOnStart()
.map { layoutInflater.inflate(id, root, attachToRoot) as T }
}
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a configurationState for a given context. The [configurationController] is
+ * supposed to give config events specific for that context.
+ */
+ fun create(
+ context: Context,
+ configurationController: ConfigurationController
+ ): ConfigurationStateImpl
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
new file mode 100644
index 0000000..b36da3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
@@ -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.systemui.common.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+/**
+ * Annotates elements that provide information from the global configuration.
+ *
+ * The global configuration is the one associted with the main display. Secondary displays will
+ * apply override to the global configuration. Elements annotated with this shouldn't be used for
+ * secondary displays.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
+
+@Module
+interface ConfigurationStateModule {
+
+ /**
+ * Deprecated: [ConfigurationState] should be injected only with the correct annotation. For
+ * now, without annotation the global config associated state is provided.
+ */
+ @Binds
+ fun provideGlobalConfigurationState(
+ @GlobalConfig configurationState: ConfigurationState
+ ): ConfigurationState
+
+ companion object {
+ @SysUISingleton
+ @Provides
+ @GlobalConfig
+ fun provideGlobalConfigurationState(
+ configStateFactory: ConfigurationStateImpl.Factory,
+ configurationController: ConfigurationController,
+ @Application context: Context,
+ ): ConfigurationState {
+ return configStateFactory.create(context, configurationController)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 8f756a2..ac496f0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -195,7 +195,7 @@
is ObservableTransitionState.Idle ->
flowOf(CommunalTransitionProgressModel.Idle(state.currentScene))
is ObservableTransitionState.Transition ->
- if (state.toScene == targetScene) {
+ if (state.toContent == targetScene) {
state.progress.map {
CommunalTransitionProgressModel.Transition(
// Clamp the progress values between 0 and 1 as actual progress
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index e04d309..c7538bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -161,7 +161,7 @@
if (
prevTransition is ObservableTransitionState.Transition &&
currentTransitionId != null &&
- idle.currentScene == prevTransition.toScene
+ idle.currentScene == prevTransition.toContent
) {
finishCurrentTransition()
} else {
@@ -219,17 +219,19 @@
prevTransition: ObservableTransitionState,
transition: ObservableTransitionState.Transition
) {
- if (prevTransition.isTransitioning(from = transition.fromScene, to = transition.toScene)) {
+ if (
+ prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
+ ) {
// This is a new transition, but exactly the same as the previous state. Skip resetting
// KTF for this case and just collect the new progress instead.
collectProgress(transition)
- } else if (transition.toScene == CommunalScenes.Communal) {
+ } else if (transition.toContent == CommunalScenes.Communal) {
if (currentToState == KeyguardState.GLANCEABLE_HUB) {
transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
startTransitionToGlanceableHub()
collectProgress(transition)
- } else if (transition.toScene == CommunalScenes.Blank) {
+ } else if (transition.toContent == CommunalScenes.Blank) {
// Another transition started before this one is completed. Transition to the
// GLANCEABLE_HUB state so that we can properly transition away from it.
transitionKtfTo(KeyguardState.GLANCEABLE_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
index 2352841f..1def5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -126,13 +126,13 @@
/** Whether currently transitioning from another scene to communal. */
private fun ObservableTransitionState.isSwipingToCommunal(): Boolean {
return this is ObservableTransitionState.Transition &&
- toScene == CommunalScenes.Communal &&
+ toContent == CommunalScenes.Communal &&
isInitiatedByUserInput
}
/** Whether currently transitioning from communal to another scene. */
private fun ObservableTransitionState.isSwipingFromCommunal(): Boolean {
return this is ObservableTransitionState.Transition &&
- fromScene == CommunalScenes.Communal &&
+ fromContent == CommunalScenes.Communal &&
isInitiatedByUserInput
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
index aed9215..83f31e5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
@@ -74,8 +74,8 @@
tag = TAG,
level = LogLevel.INFO,
messageInitializer = {
- str1 = transitionState.fromScene.toString()
- str2 = transitionState.toScene.toString()
+ str1 = transitionState.fromContent.toString()
+ str2 = transitionState.toContent.toString()
},
messagePrinter = { "Scene transition started: $str1 → $str2" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 1dd3722..3fe6669 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -21,6 +21,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
+import com.android.systemui.common.ui.GlobalConfig;
import com.android.systemui.dagger.qualifiers.PerUser;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -127,6 +128,7 @@
* Creates a ContextComponentHelper.
*/
@SysUISingleton
+ @GlobalConfig
ConfigurationController getConfigurationController();
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 411cbd5..b55108d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,6 +48,7 @@
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.common.data.CommonDataLayerModule;
+import com.android.systemui.common.ui.ConfigurationStateModule;
import com.android.systemui.common.usagestats.data.CommonUsageStatsDataLayerModule;
import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.complication.dagger.ComplicationComponent;
@@ -207,6 +208,7 @@
ClockRegistryModule.class,
CommunalModule.class,
CommonDataLayerModule.class,
+ ConfigurationStateModule.class,
CommonUsageStatsDataLayerModule.class,
ConfigurationControllerModule.class,
ConnectivityModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 39f4e31..7018f9d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -16,12 +16,14 @@
package com.android.systemui.deviceentry.domain.interactor
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
@@ -56,6 +58,7 @@
private val sceneInteractor: SceneInteractor,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
) {
/**
* Whether the device is unlocked.
@@ -126,17 +129,14 @@
},
isLockscreenEnabled,
deviceUnlockedInteractor.deviceUnlockStatus,
- isDeviceEntered) {
- isNoneAuthMethod,
- isLockscreenEnabled,
- deviceUnlockStatus,
- isDeviceEntered ->
- val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
- (isSwipeAuthMethod ||
- (deviceUnlockStatus.isUnlocked &&
- deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
- !isDeviceEntered
- }
+ isDeviceEntered
+ ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered ->
+ val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
+ (isSwipeAuthMethod ||
+ (deviceUnlockStatus.isUnlocked &&
+ deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
+ !isDeviceEntered
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -150,8 +150,16 @@
/**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
* unlock the device it will transition to bouncer.
+ *
+ * @param callback An optional callback to invoke when the attempt succeeds, fails, or is
+ * canceled
*/
- fun attemptDeviceEntry() {
+ @JvmOverloads
+ fun attemptDeviceEntry(
+ callback: IKeyguardDismissCallback? = null,
+ ) {
+ callback?.let { dismissCallbackRegistry.addCallback(it) }
+
// TODO (b/307768356),
// 1. Check if the device is already authenticated by trust agent/passive biometrics
// 2. Show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 0feb5ec..1bc91ca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -76,6 +76,7 @@
import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
@@ -122,6 +123,7 @@
private final PowerInteractor mPowerInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractorLazy;
private final Executor mMainExecutor;
private final Lazy<KeyguardStateCallbackStartable> mKeyguardStateCallbackStartableLazy;
@@ -347,7 +349,8 @@
KeyguardEnabledInteractor keyguardEnabledInteractor,
Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
- KeyguardDismissInteractor keyguardDismissInteractor) {
+ KeyguardDismissInteractor keyguardDismissInteractor,
+ Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -360,6 +363,7 @@
mSceneInteractorLazy = sceneInteractorLazy;
mMainExecutor = mainExecutor;
mKeyguardStateCallbackStartableLazy = keyguardStateCallbackStartableLazy;
+ mDeviceEntryInteractorLazy = deviceEntryInteractorLazy;
if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -484,7 +488,9 @@
public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
trace("dismiss message=" + message);
checkPermission();
- if (KeyguardWmStateRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
+ mDeviceEntryInteractorLazy.get().attemptDeviceEntry(callback);
+ } else if (KeyguardWmStateRefactor.isEnabled()) {
mKeyguardDismissInteractor.dismissKeyguardWithCallback(callback);
} else {
mKeyguardViewMediator.dismiss(callback, message);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
deleted file mode 100644
index 7e09a10..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt
+++ /dev/null
@@ -1,29 +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.keyguard
-
-import com.android.systemui.Flags
-
-/** Helper for reading or using the new picker UI flag. */
-@Suppress("NOTHING_TO_INLINE")
-object NewPickerUiKeyguardPreview {
-
- /** Is the new picker UI enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.newPickerUi()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 2af95f2..2c3b481 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -119,7 +119,8 @@
is ObservableTransitionState.Idle ->
it.currentScene == Scenes.Lockscreen
is ObservableTransitionState.Transition ->
- it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen
+ it.fromContent == Scenes.Lockscreen ||
+ it.toContent == Scenes.Lockscreen
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index ac87400..a09cd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -127,8 +127,8 @@
when (transitionState) {
is ObservableTransitionState.Transition ->
when {
- transitionState.fromScene == Scenes.Lockscreen &&
- transitionState.toScene == Scenes.Gone ->
+ transitionState.fromContent == Scenes.Lockscreen &&
+ transitionState.toContent == Scenes.Gone ->
sceneInteractor
.get()
.isTransitionUserInputOngoing
@@ -139,8 +139,8 @@
flowOf(true)
}
}
- transitionState.fromScene == Scenes.Bouncer &&
- transitionState.toScene == Scenes.Gone ->
+ transitionState.fromContent == Scenes.Bouncer &&
+ transitionState.toContent == Scenes.Gone ->
transitionState.progress.map { progress ->
progress >
FromPrimaryBouncerTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index ffd7812..f3bb829 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -111,7 +111,7 @@
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
- if (idle.currentScene == prevTransition.toScene) {
+ if (idle.currentScene == prevTransition.toContent) {
finishCurrentTransition()
} else {
val targetState =
@@ -150,7 +150,7 @@
}
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
- if (transition.fromScene == Scenes.Lockscreen) {
+ if (transition.fromContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
val currentToState =
internalTransitionInteractor.currentTransitionInfoInternal.value.to
@@ -160,7 +160,7 @@
}
startTransitionFromLockscreen()
collectProgress(transition)
- } else if (transition.toScene == Scenes.Lockscreen) {
+ } else if (transition.toContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
transitionKtfTo(UNDEFINED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index bec8f3d..f1b9cba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -93,7 +93,7 @@
blueprint.applyConstraints(this)
}
- logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+ logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
}
@@ -115,7 +115,7 @@
clone(constraintLayout)
blueprint.applyConstraints(this)
}
- logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+ logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
}
@@ -124,7 +124,7 @@
}
}
- private fun logAlphaVisibilityOfAppliedConstraintSet(
+ private fun logAlphaVisibilityScaleOfAppliedConstraintSet(
cs: ConstraintSet,
viewModel: KeyguardClockViewModel
) {
@@ -136,12 +136,15 @@
Log.i(
TAG,
"applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
- "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha}"
+ "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha} " +
+ "scale=${cs.getConstraint(smallClockViewId).transform.scaleX} "
)
Log.i(
TAG,
"applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
- "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha}"
+ "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha} " +
+ "scale=${cs.getConstraint(largeClockViewId).transform.scaleX} " +
+ "pivotX=${cs.getConstraint(largeClockViewId).transform.transformPivotX} "
)
Log.i(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index c8fe55d..be6b0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -59,6 +59,16 @@
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+internal fun ConstraintSet.setScaleX(
+ views: Iterable<View>,
+ alpha: Float,
+) = views.forEach { view -> this.setScaleX(view.id, alpha) }
+
+internal fun ConstraintSet.setScaleY(
+ views: Iterable<View>,
+ alpha: Float,
+) = views.forEach { view -> this.setScaleY(view.id, alpha) }
+
@SysUISingleton
class ClockSection
@Inject
@@ -125,6 +135,9 @@
setAlpha(getNonTargetClockFace(clock).views, 0F)
if (!keyguardClockViewModel.isLargeClockVisible.value) {
connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
+ } else {
+ setScaleX(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
+ setScaleY(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
}
}
}
@@ -205,6 +218,9 @@
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin)
connect(R.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM)
+
+ // Explicitly clear pivot to force recalculate pivot instead of using legacy value
+ setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
}
constrainWeatherClockDateIconsBarrier(constraints)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index fe4ebfe..72740d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -20,7 +20,6 @@
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.FlowTracing.traceEmissionCount
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.NewPickerUiKeyguardPreview
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,6 +28,7 @@
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
@@ -169,7 +169,7 @@
/** An observable for the view-model of the "start button" quick affordance. */
val startButton: Flow<KeyguardQuickAffordanceViewModel> =
- if (NewPickerUiKeyguardPreview.isEnabled) {
+ if (Flags.newCustomizationPickerUi()) {
previewAffordances.flatMapLatestConflated {
button(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
@@ -184,7 +184,7 @@
/** An observable for the view-model of the "end button" quick affordance. */
val endButton: Flow<KeyguardQuickAffordanceViewModel> =
- if (NewPickerUiKeyguardPreview.isEnabled) {
+ if (Flags.newCustomizationPickerUi()) {
previewAffordances.flatMapLatestConflated {
button(
position = KeyguardQuickAffordancePosition.BOTTOM_END,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 9b1ca1e..6440205 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -122,8 +122,10 @@
@Provides
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
- fun bindConfigurationController(context: Context): ConfigurationController =
- ConfigurationControllerImpl(context)
+ fun bindConfigurationController(
+ context: Context,
+ configurationControlleFactory: ConfigurationControllerImpl.Factory
+ ): ConfigurationController = configurationControlleFactory.create(context)
@Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index db988f6..6ef83e2 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -21,32 +21,18 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.TransitionKeys
import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
/** Models the UI state for the user actions for navigating to other scenes or overlays. */
-class NotificationsShadeOverlayActionsViewModel
-@AssistedInject
-constructor(
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
+class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
+ SceneActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
setActions(
mapOf(
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- Swipe.Up to UserActionResult.HideOverlay(Overlays.NotificationsShade)
- } else {
- Swipe.Down to
- UserActionResult.HideOverlay(
- overlay = Overlays.NotificationsShade,
- transitionKey = TransitionKeys.OpenBottomShade,
- )
- },
+ Swipe.Up to UserActionResult.HideOverlay(Overlays.NotificationsShade),
Back to UserActionResult.HideOverlay(Overlays.NotificationsShade),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
new file mode 100644
index 0000000..5be225c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.notifications.ui.viewmodel
+
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models UI state used to render the content of the notifications shade overlay.
+ *
+ * Different from [NotificationsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class NotificationsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+ val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+ val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+ private val sceneInteractor: SceneInteractor,
+) {
+ fun onScrimClicked() {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = "Shade scrim clicked",
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): NotificationsShadeOverlayContentViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
index 9fb09c0..572a0ca 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
@@ -22,28 +22,19 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
/**
* Models the UI state for the user actions that the user can perform to navigate to other scenes.
*/
-class NotificationsShadeSceneActionsViewModel
-@AssistedInject
-constructor(
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
+class NotificationsShadeSceneActionsViewModel @AssistedInject constructor() :
+ SceneActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
setActions(
mapOf(
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- Swipe.Up
- } else {
- Swipe.Down
- } to SceneFamilies.Home,
+ Swipe.Up to SceneFamilies.Home,
Back to SceneFamilies.Home,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index b75f180..9538392 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -21,34 +21,18 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.Overlays
-import com.android.systemui.scene.shared.model.TransitionKeys
import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
/** Models the UI state for the user actions for navigating to other scenes or overlays. */
-class QuickSettingsShadeOverlayActionsViewModel
-@AssistedInject
-constructor(
- private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
+class QuickSettingsShadeOverlayActionsViewModel @AssistedInject constructor() :
+ SceneActionsViewModel() {
override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
setActions(
buildMap {
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- put(Swipe.Up, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
- } else {
- put(
- Swipe.Down,
- UserActionResult.HideOverlay(
- overlay = Overlays.QuickSettingsShade,
- transitionKey = TransitionKeys.OpenBottomShade,
- )
- )
- }
+ put(Swipe.Up, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
put(Back, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index b8311ce..3b97d82 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -16,7 +16,8 @@
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -30,10 +31,16 @@
class QuickSettingsShadeOverlayContentViewModel
@AssistedInject
constructor(
- val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
+ val sceneInteractor: SceneInteractor,
val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
) {
+ fun onScrimClicked() {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = "Shade scrim clicked",
+ )
+ }
@AssistedFactory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
index 9956a46..9690aab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
import com.android.compose.animation.scene.Back
@@ -24,11 +22,8 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
/**
@@ -40,7 +35,6 @@
class QuickSettingsShadeSceneActionsViewModel
@AssistedInject
constructor(
- private val shadeInteractor: ShadeInteractor,
val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
) : SceneActionsViewModel() {
@@ -48,14 +42,7 @@
quickSettingsContainerViewModel.editModeViewModel.isEditing
.map { editing ->
buildMap {
- put(
- if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
- Swipe.Up
- } else {
- Swipe.Down
- },
- UserActionResult(SceneFamilies.Home)
- )
+ put(Swipe.Up, UserActionResult(SceneFamilies.Home))
if (!editing) {
put(Back, UserActionResult(SceneFamilies.Home))
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
index 924a939..5185828 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
/**
* Models UI state used to render the content of the quick settings shade scene.
@@ -32,7 +28,6 @@
class QuickSettingsShadeSceneContentViewModel
@AssistedInject
constructor(
- val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
index 14dfcc5..b0eaf9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
@@ -21,6 +21,10 @@
import com.android.traceur.PresetTraceConfigs.getDefaultConfig
import com.android.traceur.TraceConfig
+/**
+ * This class encapsulates the values that go into a customized record issue trace config, part of
+ * the RecordIssueTile feature. This class stores the last configuration chosen by power users.
+ */
class CustomTraceState(private val prefs: SharedPreferences) {
private var enabledTags: Set<String>?
diff --git a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
index 4c730a0..7a57fba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
@@ -16,8 +16,8 @@
package com.android.systemui.scene
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index a2142b6..0d24adc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -202,8 +202,8 @@
}
is ObservableTransitionState.Transition -> {
when {
- transition.toScene == scene -> transition.progress
- transition.fromScene == scene -> transition.progress.map { 1f - it }
+ transition.toContent == scene -> transition.progress
+ transition.fromContent == scene -> transition.progress.map { 1f - it }
else -> flowOf(0f)
}
}
@@ -501,7 +501,7 @@
}
val inMidTransitionFromGone =
- (transitionState.value as? ObservableTransitionState.Transition)?.fromScene ==
+ (transitionState.value as? ObservableTransitionState.Transition)?.fromContent ==
Scenes.Gone
val isChangeAllowed =
to != Scenes.Gone ||
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index 9c2b992..e51a8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -81,14 +81,14 @@
state.currentScene == Scenes.QuickSettingsShade ||
state.currentScene == Scenes.Lockscreen
is ObservableTransitionState.Transition ->
- state.toScene == Scenes.Shade ||
- state.toScene == Scenes.NotificationsShade ||
- state.toScene == Scenes.QuickSettingsShade ||
- state.toScene == Scenes.Lockscreen ||
- state.fromScene == Scenes.Shade ||
- state.fromScene == Scenes.NotificationsShade ||
- state.fromScene == Scenes.QuickSettingsShade ||
- state.fromScene == Scenes.Lockscreen
+ state.toContent == Scenes.Shade ||
+ state.toContent == Scenes.NotificationsShade ||
+ state.toContent == Scenes.QuickSettingsShade ||
+ state.toContent == Scenes.Lockscreen ||
+ state.fromContent == Scenes.Shade ||
+ state.fromContent == Scenes.NotificationsShade ||
+ state.fromContent == Scenes.QuickSettingsShade ||
+ state.fromContent == Scenes.Lockscreen
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 7eb48d6..0a7526a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -189,7 +189,7 @@
// current scene
when (state) {
is ObservableTransitionState.Idle -> state.currentScene
- is ObservableTransitionState.Transition -> state.fromScene
+ is ObservableTransitionState.Transition -> state.fromContent
}.let { it == Scenes.Shade || it == Scenes.QuickSettings }
}
.distinctUntilChanged()
@@ -220,7 +220,7 @@
}
}
is ObservableTransitionState.Transition -> {
- if (state.fromScene == Scenes.Gone) {
+ if (state.fromContent == Scenes.Gone) {
true to "scene transitioning away from Gone"
} else {
null
@@ -351,8 +351,8 @@
is ObservableTransitionState.Idle -> setOf(transitionState.currentScene)
is ObservableTransitionState.Transition ->
setOf(
- transitionState.fromScene,
- transitionState.toScene,
+ transitionState.fromContent,
+ transitionState.toContent,
)
}
val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
@@ -461,7 +461,8 @@
sceneInteractor.transitionState.value as? ObservableTransitionState.Transition
?: return@collect
if (
- transition.fromScene == Scenes.Gone && transition.toScene == Scenes.Lockscreen
+ transition.fromContent == Scenes.Gone &&
+ transition.toContent == Scenes.Lockscreen
) {
switchToScene(
targetSceneKey = Scenes.Gone,
@@ -694,8 +695,8 @@
.filterIsInstance<ObservableTransitionState.Transition>()
// Only consider user-initiated (e.g. drags) that go from bouncer to lockscreen.
.filter { transition ->
- transition.fromScene == Scenes.Bouncer &&
- transition.toScene == Scenes.Lockscreen &&
+ transition.fromContent == Scenes.Bouncer &&
+ transition.toContent == Scenes.Lockscreen &&
transition.isInitiatedByUserInput
}
.flatMapLatest { it.progress }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index aa418e6..fb53ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -78,8 +78,8 @@
tag = TAG,
level = LogLevel.INFO,
messageInitializer = {
- str1 = transitionState.fromScene.toString()
- str2 = transitionState.toScene.toString()
+ str1 = transitionState.fromContent.toString()
+ str2 = transitionState.toContent.toString()
},
messagePrinter = { "Scene transition started: $str1 → $str2" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
index ef5290f..fcf6288 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
@@ -54,7 +54,9 @@
* large screens or unfolded foldables, where notifications and quick settings are shown
* side-by-side in their own columns).
*/
- @JvmField val NotificationsShade = SceneKey("notifications_shade")
+ @Deprecated("The notifications shade scene has been replaced by an overlay")
+ @JvmField
+ val NotificationsShade = SceneKey("notifications_shade")
/**
* The quick settings scene shows the quick setting tiles.
@@ -70,7 +72,9 @@
* and one for quick settings, [NotificationsShade] and [QuickSettingsShade] scenes are used
* respectively.
*/
- @JvmField val QuickSettings = SceneKey("quick_settings")
+ @Deprecated("The quick settings shade scene has been replaced by an overlay")
+ @JvmField
+ val QuickSettings = SceneKey("quick_settings")
/**
* The quick settings shade scene shows the quick setting tiles as an overlay UI.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index be95441..b9f57f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -27,14 +27,6 @@
/** Reference to the gone/lockscreen to shade transition with split shade enabled. */
val ToSplitShade = TransitionKey("GoneToSplitShade")
- /** Reference to a scene transition that can collapse the shade scene instantly. */
- val CollapseShadeInstantly = TransitionKey("CollapseShadeInstantly")
-
- /**
- * Reference to a scene transition that brings up the shade from the bottom instead of the top.
- */
- val OpenBottomShade = TransitionKey("OpenBottomShade")
-
/**
* Reference to a scene transition that can collapse the shade scene slightly faster than a
* normal collapse would.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index c1bb6fb..8a2e274 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -6,10 +6,10 @@
import android.view.View
import android.view.WindowInsets
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index ec6513a..075599b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -44,11 +44,10 @@
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.ui.composable.ComposableScene
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.composable.SceneContainer
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -187,8 +186,7 @@
) {
SceneContainer(
viewModel = viewModel,
- sceneByKey =
- sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+ sceneByKey = sceneByKey,
overlayByKey = overlayByKey,
initialSceneKey = containerConfig.initialSceneKey,
dataSourceDelegator = dataSourceDelegator,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
index 88d4c4f..7b0e7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
@@ -16,14 +16,12 @@
package com.android.systemui.scene.ui.viewmodel
-import androidx.compose.ui.Alignment
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
@@ -47,38 +45,23 @@
// zones.
shadeMode is ShadeMode.Dual
) {
- if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
- put(
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Bottom,
- direction = SwipeDirection.Up,
- ),
- UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
- )
- } else {
- put(
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Top,
- direction = SwipeDirection.Down,
- ),
- UserActionResult(SceneFamilies.QuickSettings)
- )
- }
- }
-
- if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
- put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
- } else {
put(
- Swipe.Down,
- UserActionResult(
- SceneFamilies.NotifShade,
- ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
- )
+ Swipe(
+ pointerCount = 2,
+ fromSource = Edge.Top,
+ direction = SwipeDirection.Down,
+ ),
+ UserActionResult(SceneFamilies.QuickSettings)
)
}
+
+ put(
+ Swipe.Down,
+ UserActionResult(
+ SceneFamilies.NotifShade,
+ ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+ )
+ )
}
}
.collect { setActions(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index f1d9764..830649b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -1063,13 +1063,17 @@
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setClippingBounds();
- if (mSplitShadeEnabled) {
- // In split shade we want to pretend that QS are always collapsed so their behaviour and
- // interactions don't influence notifications as they do in portrait. But we want to set
- // 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
- mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
- } else {
- mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+ if (!SceneContainerFlag.isEnabled()) {
+ if (mSplitShadeEnabled) {
+ // In split shade we want to pretend that QS are always collapsed so their
+ // behaviour and interactions don't influence notifications as they do in portrait.
+ // But we want to set 0 explicitly in case we're rotating from non-split shade with
+ // QS expansion of 1.
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
+ } else {
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(
+ qsExpansionFraction);
+ }
}
mDepthController.setQsPanelExpansion(qsExpansionFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 23e2620..5d03a28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade
import android.view.MotionEvent
-import androidx.compose.ui.Alignment
import com.android.systemui.assist.AssistManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -25,7 +24,6 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -177,7 +175,6 @@
sceneInteractor.changeScene(
SceneFamilies.NotifShade,
"ShadeController.animateExpandShade",
- OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
)
}
@@ -185,7 +182,6 @@
sceneInteractor.changeScene(
SceneFamilies.QuickSettings,
"ShadeController.animateExpandQs",
- OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 018144b..fc8a593 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -37,10 +37,10 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.view.SceneWindowRootView
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index a4fed873..193056c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -18,7 +18,6 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -103,9 +102,6 @@
@Deprecated("Use ShadeInteractor.isQsBypassingShade instead")
val legacyExpandImmediate: StateFlow<Boolean>
- /** Whether dual shade should be aligned to the bottom (true) or to the top (false). */
- val isDualShadeAlignedToBottom: Boolean
-
/**
* Whether the shade layout should be wide (true) or narrow (false).
*
@@ -238,9 +234,6 @@
private val _isShadeLayoutWide = MutableStateFlow(false)
override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
- override val isDualShadeAlignedToBottom =
- applicationContext.resources.getBoolean(R.bool.config_dualShadeAlignedToBottom)
-
override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
_isShadeLayoutWide.value = isShadeLayoutWide
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
index 79a94a5..8467185 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -49,10 +49,10 @@
is ObservableTransitionState.Idle -> flowOf(false)
is ObservableTransitionState.Transition ->
if (
- (state.fromScene == Scenes.Shade &&
- state.toScene != Scenes.QuickSettings) ||
- (state.fromScene == Scenes.QuickSettings &&
- state.toScene != Scenes.Shade)
+ (state.fromContent == Scenes.Shade &&
+ state.toContent != Scenes.QuickSettings) ||
+ (state.fromContent == Scenes.QuickSettings &&
+ state.toContent != Scenes.Shade)
) {
state.isUserInputOngoing.map { !it }
} else {
@@ -71,10 +71,10 @@
is ObservableTransitionState.Transition ->
if (
state.isInitiatedByUserInput &&
- (state.fromScene == Scenes.Shade ||
- state.toScene == Scenes.Shade ||
- state.fromScene == Scenes.QuickSettings ||
- state.toScene == Scenes.QuickSettings)
+ (state.fromContent == Scenes.Shade ||
+ state.toContent == Scenes.Shade ||
+ state.fromContent == Scenes.QuickSettings ||
+ state.toContent == Scenes.QuickSettings)
) {
state.isUserInputOngoing.map { !it }
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 45f359e..73e86a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade.domain.interactor
-import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -70,9 +69,6 @@
* wide as the entire screen.
*/
val isShadeLayoutWide: StateFlow<Boolean>
-
- /** How to align the shade content. */
- val shadeAlignment: ShadeAlignment
}
/** ShadeInteractor methods with implementations that differ between non-empty impls. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index e77aca9..d51fd28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -17,7 +17,6 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -48,5 +47,4 @@
override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
override val isShadeLayoutWide: StateFlow<Boolean> = inactiveFlowBoolean
- override val shadeAlignment: ShadeAlignment = ShadeAlignment.Top
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index d64b21f..3552092 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -26,7 +26,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
@@ -114,15 +113,6 @@
initialValue = determineShadeMode(isShadeLayoutWide.value)
)
- override val shadeAlignment: ShadeAlignment
- get() {
- return if (shadeRepository.isDualShadeAlignedToBottom) {
- ShadeAlignment.Bottom
- } else {
- ShadeAlignment.Top
- }
- }
-
override val isExpandToQsEnabled: Flow<Boolean> =
combine(
disableFlagsRepository.disableFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 6a21531..e84cfa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -90,8 +90,8 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.toScene == quickSettingsScene &&
- state.fromScene != notificationsScene
+ state.toContent == quickSettingsScene &&
+ state.fromContent != notificationsScene
}
}
.distinctUntilChanged()
@@ -99,14 +99,17 @@
.distinctUntilChanged()
override val isQsFullscreen: Flow<Boolean> =
- sceneInteractor
- .resolveSceneFamily(SceneFamilies.QuickSettings)
- .flatMapLatestConflated { quickSettingsScene ->
+ combine(
+ shadeRepository.isShadeLayoutWide,
+ sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+ ::Pair
+ )
+ .flatMapLatestConflated { (isShadeLayoutWide, quickSettingsScene) ->
sceneInteractor.transitionState
.map { state ->
when (state) {
is ObservableTransitionState.Idle ->
- state.currentScene == quickSettingsScene
+ !isShadeLayoutWide && state.currentScene == quickSettingsScene
is ObservableTransitionState.Transition -> false
}
}
@@ -147,9 +150,9 @@
flowOf(0f)
}
is ObservableTransitionState.Transition ->
- if (state.toScene == resolvedSceneKey) {
+ if (state.toContent == resolvedSceneKey) {
state.progress
- } else if (state.fromScene == resolvedSceneKey) {
+ } else if (state.fromContent == resolvedSceneKey) {
state.progress.map { progress -> 1 - progress }
} else {
flowOf(0f)
@@ -172,8 +175,8 @@
is ObservableTransitionState.Transition ->
sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey ->
state.isInitiatedByUserInput &&
- (state.toScene == resolvedSceneKey ||
- state.fromScene == resolvedSceneKey)
+ (state.toContent == resolvedSceneKey ||
+ state.fromContent == resolvedSceneKey)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
deleted file mode 100644
index abf1f4c..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ /dev/null
@@ -1,68 +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.shade.ui.viewmodel
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/**
- * Models UI state and handles user input for the overlay shade UI, which shows a shade as an
- * overlay on top of another scene UI.
- */
-class OverlayShadeViewModel
-@AssistedInject
-constructor(
- private val sceneInteractor: SceneInteractor,
- shadeInteractor: ShadeInteractor,
-) : ExclusiveActivatable() {
- private val _backgroundScene = MutableStateFlow(Scenes.Lockscreen)
- /** The scene to show in the background when the overlay shade is open. */
- val backgroundScene: StateFlow<SceneKey> = _backgroundScene.asStateFlow()
-
- /** Dictates the alignment of the overlay shade panel on the screen. */
- val panelAlignment = shadeInteractor.shadeAlignment
-
- override suspend fun onActivated(): Nothing {
- sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collect { sceneKey ->
- _backgroundScene.value = sceneKey
- }
- awaitCancellation()
- }
-
- /** Notifies that the user has clicked the semi-transparent background scrim. */
- fun onScrimClicked() {
- sceneInteractor.changeScene(
- toScene = SceneFamilies.Home,
- loggingReason = "Shade scrim clicked",
- )
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): OverlayShadeViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 2b44c2f..87f360e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -415,8 +415,8 @@
if (!levelEquals) {
setImageLevel(icon.iconLevel);
}
- if (usesModeIcons()) {
- setScaleType(icon.shape == Shape.FIXED_SPACE ? ScaleType.FIT_CENTER : ScaleType.CENTER);
+ if (usesModeIcons() && icon.shape == Shape.FIXED_SPACE) {
+ setScaleType(ScaleType.FIT_CENTER);
}
if (!visibilityEquals) {
setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 2f3719a..1431b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -533,8 +533,15 @@
if (mDozeAmount == 1.0f && !isPulseExpanding()) {
return mShelf.getHeight();
}
- int height = (int) Math.max(mLayoutMinHeight,
- Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+ int height;
+ if (SceneContainerFlag.isEnabled()) {
+ // TODO(b/192348384): This is probably incorrect as mContentHeight is not up to date.
+ // Consider removing usages of getInnerHeight in flexiglass if possible.
+ height = (int) Math.min(mLayoutHeight, mContentHeight) - mTopPadding;
+ } else {
+ height = (int) Math.max(mLayoutMinHeight,
+ Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+ }
if (ignorePulseHeight) {
return height;
}
@@ -571,6 +578,7 @@
}
public void setLayoutMinHeight(int layoutMinHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mLayoutMinHeight = layoutMinHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 925ebf3..b9628e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1308,8 +1308,10 @@
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
- ? getLayoutMinHeightInternal() : 0);
+ if (!SceneContainerFlag.isEnabled()) {
+ mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
+ ? getLayoutMinHeightInternal() : 0);
+ }
}
/**
@@ -2629,6 +2631,9 @@
}
private void updateScrollability() {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
@@ -2638,6 +2643,7 @@
}
private void updateForwardAndBackwardScrollability() {
+ SceneContainerFlag.assertInLegacyMode();
boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom();
boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop();
boolean changed = forwardScrollable != mForwardScrollable
@@ -2795,6 +2801,7 @@
}
private int getLayoutMinHeightInternal() {
+ SceneContainerFlag.assertInLegacyMode();
if (isHeadsUpTransition()) {
ExpandableNotificationRow trackedHeadsUpRow = mAmbientState.getTrackedHeadsUpRow();
if (trackedHeadsUpRow.isAboveShelf()) {
@@ -5147,10 +5154,12 @@
}
boolean isQsFullScreen() {
+ SceneContainerFlag.assertInLegacyMode();
return mQsFullScreen;
}
public void setQsExpansionFraction(float qsExpansionFraction) {
+ SceneContainerFlag.assertInLegacyMode();
boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
&& (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
mQsExpansionFraction = qsExpansionFraction;
@@ -5194,6 +5203,7 @@
}
private void updateOnScrollChange() {
+ SceneContainerFlag.assertInLegacyMode();
if (mScrollListener != null) {
mScrollListener.accept(mOwnScrollY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e112c99..bcdc3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1275,6 +1275,7 @@
}
public void setQsExpansionFraction(float expansionFraction) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setQsExpansionFraction(expansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 2e1ab38..bb5aa23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -20,16 +20,17 @@
import android.graphics.Rect
import android.os.LocaleList
import android.view.View.LAYOUT_DIRECTION_RTL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-@SysUISingleton
-class ConfigurationControllerImpl @Inject constructor(
- @Application context: Context,
- ) : ConfigurationController {
+class ConfigurationControllerImpl
+@AssistedInject
+constructor(
+ @Assisted private val context: Context,
+) : ConfigurationController {
private val listeners: MutableList<ConfigurationListener> = ArrayList()
private val lastConfig = Configuration()
@@ -40,18 +41,17 @@
private val inCarMode: Boolean
private var uiMode: Int = 0
private var localeList: LocaleList? = null
- private val context: Context
private var layoutDirection: Int
private var orientation = Configuration.ORIENTATION_UNDEFINED
init {
val currentConfig = context.resources.configuration
- this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
smallestScreenWidth = currentConfig.smallestScreenWidthDp
maxBounds.set(currentConfig.windowConfiguration.maxBounds)
- inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+ inCarMode =
+ currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
localeList = currentConfig.locales
@@ -60,29 +60,20 @@
override fun notifyThemeChanged() {
// Avoid concurrent modification exception
- val listeners = synchronized(this.listeners) {
- ArrayList(this.listeners)
- }
+ val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onThemeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
}
override fun onConfigurationChanged(newConfig: Configuration) {
// Avoid concurrent modification exception
- val listeners = synchronized(this.listeners) {
- ArrayList(this.listeners)
- }
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onConfigChanged(newConfig)
- }
+ val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onConfigChanged(newConfig) }
val fontScale = newConfig.fontScale
val density = newConfig.densityDpi
val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
val uiModeChanged = uiMode != this.uiMode
- if (density != this.density || fontScale != this.fontScale ||
- inCarMode && uiModeChanged) {
+ if (density != this.density || fontScale != this.fontScale || inCarMode && uiModeChanged) {
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onDensityOrFontScaleChanged()
}
@@ -105,17 +96,13 @@
// would be a direct reference to windowConfiguration.maxBounds, so the if statement
// above would always fail. See b/245799099 for more information.
this.maxBounds.set(maxBounds)
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onMaxBoundsChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onMaxBoundsChanged() }
}
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onLocaleListChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onLocaleListChanged() }
}
if (uiModeChanged) {
@@ -124,9 +111,7 @@
context.theme.applyStyle(context.themeResId, true)
this.uiMode = uiMode
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onUiModeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onUiModeChanged() }
}
if (layoutDirection != newConfig.layoutDirection) {
@@ -137,9 +122,7 @@
}
if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
- listeners.filterForEach({ this.listeners.contains(it) }) {
- it.onThemeChanged()
- }
+ listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
}
val newOrientation = newConfig.orientation
@@ -152,16 +135,12 @@
}
override fun addCallback(listener: ConfigurationListener) {
- synchronized(listeners) {
- listeners.add(listener)
- }
+ synchronized(listeners) { listeners.add(listener) }
listener.onDensityOrFontScaleChanged()
}
override fun removeCallback(listener: ConfigurationListener) {
- synchronized(listeners) {
- listeners.remove(listener)
- }
+ synchronized(listeners) { listeners.remove(listener) }
}
override fun isLayoutRtl(): Boolean {
@@ -176,6 +155,15 @@
else -> "err"
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a [ConfigurationController] that uses [context] to resolve the current
+ * configuration and resources.
+ */
+ fun create(context: Context): ConfigurationControllerImpl
+ }
}
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
index 90ebaf2..8f4279e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.GlobalConfig
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -26,7 +27,7 @@
class ConfigurationControllerStartable
@Inject
constructor(
- private val configurationController: ConfigurationController,
+ @GlobalConfig private val configurationController: ConfigurationController,
private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
) : CoreStartable {
override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 6cebcbd..b81af86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -23,7 +23,9 @@
import com.android.internal.R;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.systemui.common.ui.GlobalConfig;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.log.LogBuffer;
@@ -103,9 +105,12 @@
@Binds
CastController provideCastController(CastControllerImpl controllerImpl);
- /** */
+ /**
+ * @deprecated: unscoped configuration controller shouldn't be injected as it might lead to
+ * wrong updates in case of secondary displays.
+ */
@Binds
- ConfigurationController bindConfigurationController(ConfigurationControllerImpl impl);
+ ConfigurationController bindConfigurationController(@GlobalConfig ConfigurationController impl);
/** */
@Binds
@@ -181,6 +186,15 @@
DevicePostureControllerImpl devicePostureControllerImpl);
/** */
+ @Provides
+ @SysUISingleton
+ @GlobalConfig
+ static ConfigurationController provideGlobalConfigurationController(
+ @Application Context context, ConfigurationControllerImpl.Factory factory) {
+ return factory.create(context);
+ }
+
+ /** */
@SysUISingleton
@Provides
static AccessPointControllerImpl provideAccessPointControllerImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index cacb384..0e88f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.ui.dialog.composable
+import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -30,8 +31,10 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
@@ -44,12 +47,16 @@
@Composable
fun ModeTile(viewModel: ModeTileViewModel) {
- val tileColor =
- if (viewModel.enabled) MaterialTheme.colorScheme.primary
- else MaterialTheme.colorScheme.surfaceVariant
- val contentColor =
- if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
- else MaterialTheme.colorScheme.onSurfaceVariant
+ val tileColor: Color by
+ animateColorAsState(
+ if (viewModel.enabled) MaterialTheme.colorScheme.primary
+ else MaterialTheme.colorScheme.surfaceVariant
+ )
+ val contentColor: Color by
+ animateColorAsState(
+ if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
+ else MaterialTheme.colorScheme.onSurfaceVariant
+ )
CompositionLocalProvider(LocalContentColor provides contentColor) {
Surface(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 487432e..c0d8be3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -92,6 +92,8 @@
private FakeFeatureFlags mFeatureFlags;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private UserActivityNotifier mUserActivityNotifier;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
private KosmosJavaAdapter mKosmosJavaAdapter = new KosmosJavaAdapter(this);
private final FakeMSDLPlayer mMSDLPlayer = mKosmosJavaAdapter.getMsdlPlayer();
@@ -117,7 +119,8 @@
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
- mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor, mMSDLPlayer) {
+ mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor, mMSDLPlayer,
+ mUserActivityNotifier) {
@Override
void resetState() {
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index c43a184..873bc2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -103,6 +103,7 @@
@Mock lateinit var deleteButton: NumPadButton
@Mock lateinit var enterButton: View
@Mock lateinit var uiEventLogger: UiEventLogger
+ @Mock lateinit var mUserActivityNotifier: UserActivityNotifier
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
@@ -151,6 +152,7 @@
uiEventLogger,
keyguardKeyboardInteractor,
null,
+ mUserActivityNotifier
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index ea6c1bc..f141a49 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -69,6 +69,7 @@
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
private val updateMonitorCallbackArgumentCaptor =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
@@ -103,6 +104,7 @@
mSelectedUserInteractor,
keyguardKeyboardInteractor,
null,
+ mUserActivityNotifier
)
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 c26365d..a03c839 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -63,6 +63,7 @@
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
@Before
fun setup() {
@@ -98,6 +99,7 @@
mSelectedUserInteractor,
keyguardKeyboardInteractor,
null,
+ mUserActivityNotifier
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9d9e5be62..3ccb989 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -34,14 +34,16 @@
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -57,29 +59,28 @@
@RunWith(AndroidJUnit4::class)
class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
private lateinit var viewModel: StickyKeysIndicatorViewModel
private val inputManager = mock<InputManager>()
private val keyboardRepository = FakeKeyboardRepository()
- private val secureSettings = FakeSettings()
+ private val secureSettings = kosmos.fakeSettings
private val userRepository = Kosmos().fakeUserRepository
private val captor =
ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
@Before
fun setup() {
- val settingsRepository = UserAwareSecureSettingsRepositoryImpl(
- secureSettings,
- userRepository,
- dispatcher
- )
- val stickyKeysRepository = StickyKeysRepositoryImpl(
- inputManager,
- dispatcher,
- settingsRepository,
- mock<StickyKeysLogger>()
- )
+ val settingsRepository =
+ UserAwareSecureSettingsRepositoryImpl(secureSettings, userRepository, dispatcher)
+ val stickyKeysRepository =
+ StickyKeysRepositoryImpl(
+ inputManager,
+ dispatcher,
+ settingsRepository,
+ mock<StickyKeysLogger>()
+ )
setStickyKeySetting(enabled = false)
viewModel =
StickyKeysIndicatorViewModel(
@@ -182,16 +183,16 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- ALT to false,
- META to false,
- SHIFT to false))
+ setStickyKeys(mapOf(ALT to false, META to false, SHIFT to false))
- assertThat(stickyKeys).isEqualTo(mapOf(
- ALT to Locked(false),
- META to Locked(false),
- SHIFT to Locked(false),
- ))
+ assertThat(stickyKeys)
+ .isEqualTo(
+ mapOf(
+ ALT to Locked(false),
+ META to Locked(false),
+ SHIFT to Locked(false),
+ )
+ )
}
}
@@ -201,9 +202,7 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- ALT to false,
- ALT to true))
+ setStickyKeys(mapOf(ALT to false, ALT to true))
assertThat(stickyKeys).isEqualTo(mapOf(ALT to Locked(true)))
}
@@ -215,17 +214,23 @@
val stickyKeys by collectLastValue(viewModel.indicatorContent)
setStickyKeysActive()
- setStickyKeys(mapOf(
- META to false,
- SHIFT to false, // shift is sticky but not locked
- CTRL to false))
+ setStickyKeys(
+ mapOf(
+ META to false,
+ SHIFT to false, // shift is sticky but not locked
+ CTRL to false
+ )
+ )
val previousShiftIndex = stickyKeys?.toList()?.indexOf(SHIFT to Locked(false))
- setStickyKeys(mapOf(
- SHIFT to false,
- SHIFT to true, // shift is now locked
- META to false,
- CTRL to false))
+ setStickyKeys(
+ mapOf(
+ SHIFT to false,
+ SHIFT to true, // shift is now locked
+ META to false,
+ CTRL to false
+ )
+ )
assertThat(stickyKeys?.toList()?.indexOf(SHIFT to Locked(true)))
.isEqualTo(previousShiftIndex)
}
@@ -247,17 +252,27 @@
StickyModifierState() {
private fun isOn(key: ModifierKey) = keys.any { it.key == key && !it.value }
+
private fun isLocked(key: ModifierKey) = keys.any { it.key == key && it.value }
override fun isAltGrModifierLocked() = isLocked(ALT_GR)
+
override fun isAltGrModifierOn() = isOn(ALT_GR)
+
override fun isAltModifierLocked() = isLocked(ALT)
+
override fun isAltModifierOn() = isOn(ALT)
+
override fun isCtrlModifierLocked() = isLocked(CTRL)
+
override fun isCtrlModifierOn() = isOn(CTRL)
+
override fun isMetaModifierLocked() = isLocked(META)
+
override fun isMetaModifierOn() = isOn(META)
+
override fun isShiftModifierLocked() = isLocked(SHIFT)
+
override fun isShiftModifierOn() = isOn(SHIFT)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 29cd9a2..fa69fdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -50,6 +50,8 @@
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -64,12 +66,12 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -87,6 +89,11 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class CustomizationProviderTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val fakeSettings = kosmos.unconfinedDispatcherFakeSettings
+
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@@ -104,9 +111,6 @@
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var underTest: CustomizationProvider
- private lateinit var testScope: TestScope
-
- private val kosmos = testKosmos()
@Before
fun setUp() {
@@ -120,8 +124,6 @@
biometricSettingsRepository = FakeBiometricSettingsRepository()
underTest = CustomizationProvider()
- val testDispatcher = UnconfinedTestDispatcher()
- testScope = TestScope(testDispatcher)
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -170,7 +172,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = fakeSettings,
selectionsManager = localUserSelectionManager,
),
dumpManager = mock(),
@@ -216,7 +218,7 @@
mainDispatcher = testDispatcher,
backgroundHandler = backgroundHandler,
)
- underTest.mainDispatcher = UnconfinedTestDispatcher()
+ underTest.mainDispatcher = testDispatcher
underTest.attachInfoForTesting(
context,
@@ -319,6 +321,7 @@
),
)
)
+ runCurrent()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
index af5187d..1e9db64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
@@ -25,15 +25,14 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -44,12 +43,12 @@
@SmallTest
class KeyguardClockRepositoryTest : SysuiTestCase() {
- private lateinit var scheduler: TestCoroutineScheduler
- private lateinit var dispatcher: CoroutineDispatcher
- private lateinit var scope: TestScope
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val scope = kosmos.testScope
+ private val fakeSettings = kosmos.fakeSettings
private lateinit var underTest: KeyguardClockRepository
- private lateinit var fakeSettings: FakeSettings
@Mock private lateinit var clockRegistry: ClockRegistry
@Mock private lateinit var clockEventController: ClockEventController
private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
@@ -57,10 +56,6 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeSettings = FakeSettings()
- scheduler = TestCoroutineScheduler()
- dispatcher = StandardTestDispatcher(scheduler)
- scope = TestScope(dispatcher)
underTest =
KeyguardClockRepositoryImpl(
fakeSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
index 8b8a6cb..5a597fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
@@ -21,14 +21,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -38,23 +36,18 @@
@SmallTest
class KeyguardSmartspaceRepositoryImplTest : SysuiTestCase() {
- private lateinit var scheduler: TestCoroutineScheduler
- private lateinit var dispatcher: CoroutineDispatcher
- private lateinit var scope: TestScope
+ private val kosmos = testKosmos()
+ private val scope = kosmos.testScope
+ private val fakeSettings = kosmos.fakeSettings
private lateinit var underTest: KeyguardSmartspaceRepository
- private lateinit var fakeSettings: FakeSettings
private lateinit var fakeUserTracker: FakeUserTracker
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeSettings = FakeSettings()
fakeUserTracker = FakeUserTracker()
fakeSettings.userId = fakeUserTracker.userId
- scheduler = TestCoroutineScheduler()
- dispatcher = StandardTestDispatcher(scheduler)
- scope = TestScope(dispatcher)
underTest =
KeyguardSmartspaceRepositoryImpl(
context = context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index d13419e..1929cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,6 +54,8 @@
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -69,13 +71,11 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -93,6 +93,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@@ -108,7 +113,6 @@
private lateinit var underTest: KeyguardBottomAreaViewModel
- private lateinit var testScope: TestScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -116,8 +120,6 @@
private lateinit var dockManager: DockManagerFake
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private val kosmos = testKosmos()
-
init {
mSetFlagsRule.setFlagsParameterization(flags)
}
@@ -162,8 +164,6 @@
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
val localUserSelectionManager =
KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
@@ -199,7 +199,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 07f7557..720f2e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -72,7 +72,7 @@
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.math.min
import kotlin.test.assertEquals
@@ -94,6 +94,10 @@
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var expandable: Expandable
@@ -151,11 +155,8 @@
private lateinit var glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel
- private val kosmos = testKosmos()
-
private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
- private val testScope = kosmos.testScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -244,7 +245,7 @@
KeyguardQuickAffordanceLegacySettingSyncer(
scope = testScope.backgroundScope,
backgroundDispatcher = kosmos.testDispatcher,
- secureSettings = FakeSettings(),
+ secureSettings = settings,
selectionsManager = localUserSelectionManager,
),
configs =
@@ -403,7 +404,7 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_NEW_PICKER_UI)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
fun startButton_inPreviewMode_onPreviewQuickAffordanceSelected() =
testScope.runTest {
underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 3459645..f4c2b47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -84,7 +84,7 @@
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -141,6 +141,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
@EnableSceneContainer
class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val settings = kosmos.fakeSettings
+
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock lateinit var controller: MediaController
@Mock lateinit var transportControls: MediaController.TransportControls
@@ -193,9 +198,6 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
- private val testDispatcher = kosmos.testDispatcher
- private val testScope = kosmos.testScope
private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
private val activityStarter = kosmos.activityStarter
private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
@@ -203,7 +205,6 @@
private val mediaFilterRepository = kosmos.mediaFilterRepository
private val mediaDataFilter = kosmos.mediaDataFilter
- private val settings = FakeSettings()
private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
private val originalSmartspaceSetting =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 46c66e0..03667cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -72,9 +73,8 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Locale
@@ -84,9 +84,7 @@
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -120,7 +118,9 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class MediaCarouselControllerTest : SysuiTestCase() {
- val kosmos = testKosmos()
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val secureSettings = kosmos.unconfinedDispatcherFakeSettings
@Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
@Mock lateinit var mediaViewControllerFactory: Provider<MediaViewController>
@@ -142,7 +142,6 @@
@Mock lateinit var mediaFlags: MediaFlags
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock lateinit var globalSettings: GlobalSettings
- private lateinit var secureSettings: SecureSettings
private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
@Captor
@@ -154,7 +153,6 @@
private val clock = FakeSystemClock()
private lateinit var bgExecutor: FakeExecutor
- private lateinit var testDispatcher: TestDispatcher
private lateinit var mediaCarouselController: MediaCarouselController
private var originalResumeSetting =
@@ -163,10 +161,8 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- secureSettings = FakeSettings()
context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK))
bgExecutor = FakeExecutor(clock)
- testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
applicationScope = kosmos.applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
new file mode 100644
index 0000000..d8618fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.recordissue
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.traceur.PresetTraceConfigs
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+/**
+ * CustomTraceState is customized Traceur settings for power users. These settings determine what
+ * tracing is used during the Record Issue Quick Settings flow. This class tests that those features
+ * are persistently and accurately stored across sessions.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class CustomTraceStateTest : SysuiTestCase() {
+
+ private lateinit var underTest: CustomTraceState
+
+ @Before
+ fun setup() {
+ underTest = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0))
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun trace_config_is_stored_accurately() {
+ val expected = PresetTraceConfigs.getUiConfig()
+
+ underTest.traceConfig = expected
+
+ val actual = underTest.traceConfig
+ Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+ Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+ Truth.assertThat(actual.maxLongTraceDurationMinutes)
+ .isEqualTo(expected.maxLongTraceDurationMinutes)
+ Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+ Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+ Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+ Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+ Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+ }
+
+ @Test
+ fun trace_config_is_persistently_stored_between_instances() {
+ val expected = PresetTraceConfigs.getUiConfig()
+
+ underTest.traceConfig = expected
+
+ val actual = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0)).traceConfig
+ Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+ Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+ Truth.assertThat(actual.maxLongTraceDurationMinutes)
+ .isEqualTo(expected.maxLongTraceDurationMinutes)
+ Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+ Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+ Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+ Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+ Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index fadb1d7..b65a902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -156,6 +156,25 @@
}
@Test
+ fun qsFullscreen_falseWhenIdleSplitShadeQs() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isQsFullscreen)
+
+ // WHEN split shade is enabled and Idle on QuickSettings scene
+ shadeTestUtil.setSplitShade(true)
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.QuickSettings)
+ )
+ sceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ // THEN QS is not fullscreen
+ Truth.assertThat(actual).isFalse()
+ }
+
+ @Test
fun qsFullscreen_trueWhenIdleQS() =
testScope.runTest {
val actual by collectLastValue(underTest.isQsFullscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 37a73cf..c235954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -24,21 +24,21 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,143 +52,153 @@
@RunWith(AndroidJUnit4::class)
class UserRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testDispatcher = kosmos.unconfinedTestDispatcher
+ private val testScope = kosmos.unconfinedTestScope
+ private val globalSettings = kosmos.unconfinedDispatcherFakeGlobalSettings
+
@Mock private lateinit var manager: UserManager
private lateinit var underTest: UserRepositoryImpl
- private lateinit var globalSettings: FakeGlobalSettings
private lateinit var tracker: FakeUserTracker
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- globalSettings = FakeGlobalSettings()
tracker = FakeUserTracker()
}
@Test
- fun userSwitcherSettings() = runSelfCancelingTest {
- setUpGlobalSettings(
- isSimpleUserSwitcher = true,
- isAddUsersFromLockscreen = true,
- isUserSwitcherEnabled = true,
- )
- underTest = create(this)
+ fun userSwitcherSettings() =
+ testScope.runTest {
+ setUpGlobalSettings(
+ isSimpleUserSwitcher = true,
+ isAddUsersFromLockscreen = true,
+ isUserSwitcherEnabled = true,
+ )
+ underTest = create(testScope.backgroundScope)
+ var value: UserSwitcherSettingsModel? = null
+ val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
- var value: UserSwitcherSettingsModel? = null
- underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = true,
+ expectedAddUsersFromLockscreen = true,
+ expectedUserSwitcherEnabled = true,
+ )
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = true,
- expectedAddUsersFromLockscreen = true,
- expectedUserSwitcherEnabled = true,
- )
-
- setUpGlobalSettings(
- isSimpleUserSwitcher = false,
- isAddUsersFromLockscreen = true,
- isUserSwitcherEnabled = true,
- )
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = false,
- expectedAddUsersFromLockscreen = true,
- expectedUserSwitcherEnabled = true,
- )
- }
+ setUpGlobalSettings(
+ isSimpleUserSwitcher = false,
+ isAddUsersFromLockscreen = true,
+ isUserSwitcherEnabled = true,
+ )
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = true,
+ expectedUserSwitcherEnabled = true,
+ )
+ job.cancel()
+ }
@Test
- fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
- underTest = create(this)
+ fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
- var value: UserSwitcherSettingsModel? = null
- underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+ var value: UserSwitcherSettingsModel? = null
+ val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
- assertUserSwitcherSettings(
- model = value,
- expectedSimpleUserSwitcher = false,
- expectedAddUsersFromLockscreen = false,
- expectedUserSwitcherEnabled =
- context.resources.getBoolean(
- com.android.internal.R.bool.config_showUserSwitcherByDefault
- ),
- )
- }
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = false,
+ expectedUserSwitcherEnabled =
+ context.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ ),
+ )
+ job.cancel()
+ }
@Test
- fun refreshUsers() = runSelfCancelingTest {
- val mainUserId = 10
- val mainUser = mock(UserHandle::class.java)
- whenever(manager.mainUser).thenReturn(mainUser)
- whenever(mainUser.identifier).thenReturn(mainUserId)
+ fun refreshUsers() =
+ testScope.runTest {
+ val mainUserId = 10
+ val mainUser = mock(UserHandle::class.java)
+ whenever(manager.mainUser).thenReturn(mainUser)
+ whenever(mainUser.identifier).thenReturn(mainUserId)
- underTest = create(this)
- val initialExpectedValue =
- setUpUsers(
- count = 3,
- selectedIndex = 0,
- )
- var userInfos: List<UserInfo>? = null
- var selectedUserInfo: UserInfo? = null
- underTest.userInfos.onEach { userInfos = it }.launchIn(this)
- underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+ underTest = create(testScope.backgroundScope)
+ val initialExpectedValue =
+ setUpUsers(
+ count = 3,
+ selectedIndex = 0,
+ )
+ var userInfos: List<UserInfo>? = null
+ var selectedUserInfo: UserInfo? = null
+ val job1 = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+ val job2 = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(initialExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(initialExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
- val secondExpectedValue =
- setUpUsers(
- count = 4,
- selectedIndex = 1,
- )
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(secondExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+ val secondExpectedValue =
+ setUpUsers(
+ count = 4,
+ selectedIndex = 1,
+ )
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(secondExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
- val selectedNonGuestUserId = selectedUserInfo?.id
- val thirdExpectedValue =
- setUpUsers(
- count = 2,
- isLastGuestUser = true,
- selectedIndex = 1,
- )
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(thirdExpectedValue)
- assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
- assertThat(selectedUserInfo?.isGuest).isTrue()
- assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
- assertThat(underTest.mainUserId).isEqualTo(mainUserId)
- }
+ val selectedNonGuestUserId = selectedUserInfo?.id
+ val thirdExpectedValue =
+ setUpUsers(
+ count = 2,
+ isLastGuestUser = true,
+ selectedIndex = 1,
+ )
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(thirdExpectedValue)
+ assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
+ assertThat(selectedUserInfo?.isGuest).isTrue()
+ assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
+ assertThat(underTest.mainUserId).isEqualTo(mainUserId)
+ job1.cancel()
+ job2.cancel()
+ }
@Test
- fun refreshUsers_sortsByCreationTime_guestUserLast() = runSelfCancelingTest {
- underTest = create(this)
- val unsortedUsers =
- setUpUsers(
- count = 3,
- selectedIndex = 0,
- isLastGuestUser = true,
- )
- unsortedUsers[0].creationTime = 999
- unsortedUsers[1].creationTime = 900
- unsortedUsers[2].creationTime = 950
- val expectedUsers =
- listOf(
- unsortedUsers[1],
- unsortedUsers[0],
- unsortedUsers[2], // last because this is the guest
- )
- var userInfos: List<UserInfo>? = null
- underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+ fun refreshUsers_sortsByCreationTime_guestUserLast() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ val unsortedUsers =
+ setUpUsers(
+ count = 3,
+ selectedIndex = 0,
+ isLastGuestUser = true,
+ )
+ unsortedUsers[0].creationTime = 999
+ unsortedUsers[1].creationTime = 900
+ unsortedUsers[2].creationTime = 950
+ val expectedUsers =
+ listOf(
+ unsortedUsers[1],
+ unsortedUsers[0],
+ unsortedUsers[2], // last because this is the guest
+ )
+ var userInfos: List<UserInfo>? = null
+ val job = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
- underTest.refreshUsers()
- assertThat(userInfos).isEqualTo(expectedUsers)
- }
+ underTest.refreshUsers()
+ assertThat(userInfos).isEqualTo(expectedUsers)
+ job.cancel()
+ }
private fun setUpUsers(
count: Int,
@@ -206,58 +216,68 @@
tracker.set(userInfos, selectedIndex)
return userInfos
}
- @Test
- fun userTrackerCallback_updatesSelectedUserInfo() = runSelfCancelingTest {
- underTest = create(this)
- var selectedUserInfo: UserInfo? = null
- underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- setUpUsers(
- count = 2,
- selectedIndex = 0,
- )
- tracker.onProfileChanged()
- assertThat(selectedUserInfo?.id).isEqualTo(0)
- setUpUsers(
- count = 2,
- selectedIndex = 1,
- )
- tracker.onProfileChanged()
- assertThat(selectedUserInfo?.id).isEqualTo(1)
- }
@Test
- fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
- underTest = create(this)
- var selectedUser: SelectedUserModel? = null
- underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
- setUpUsers(count = 2, selectedIndex = 1)
+ fun userTrackerCallback_updatesSelectedUserInfo() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ var selectedUserInfo: UserInfo? = null
+ val job = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
- // WHEN the user is changing
- tracker.onUserChanging(userId = 1)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 0,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id).isEqualTo(0)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 1,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id).isEqualTo(1)
+ job.cancel()
+ }
- // THEN the selection status is IN_PROGRESS
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ @Test
+ fun userTrackerCallback_updatesSelectionStatus() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ var selectedUser: SelectedUserModel? = null
+ val job = underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
- // WHEN the user has finished changing
- tracker.onUserChanged(userId = 1)
+ setUpUsers(count = 2, selectedIndex = 1)
- // THEN the selection status is COMPLETE
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+ // WHEN the user is changing
+ tracker.onUserChanging(userId = 1)
- tracker.onProfileChanged()
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+ // THEN the selection status is IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
- setUpUsers(count = 2, selectedIndex = 0)
+ // WHEN the user has finished changing
+ tracker.onUserChanged(userId = 1)
- tracker.onUserChanging(userId = 0)
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ // THEN the selection status is COMPLETE
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
- // WHEN a profile change occurs while a user is changing
- tracker.onProfileChanged()
+ tracker.onProfileChanged()
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
- // THEN the selection status remains as IN_PROGRESS
- assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
- }
+ setUpUsers(count = 2, selectedIndex = 0)
+
+ tracker.onUserChanging(userId = 0)
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN a profile change occurs while a user is changing
+ tracker.onProfileChanged()
+
+ // THEN the selection status remains as IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus)
+ .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ job.cancel()
+ }
private fun createUserInfo(
id: Int,
@@ -308,26 +328,13 @@
assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
}
- /**
- * Executes the given block of execution within the scope of a dedicated [CoroutineScope] which
- * is then automatically canceled and cleaned-up.
- */
- private fun runSelfCancelingTest(
- block: suspend CoroutineScope.() -> Unit,
- ) =
- runBlocking(Dispatchers.Main.immediate) {
- val scope = CoroutineScope(coroutineContext + Job())
- block(scope)
- scope.cancel()
- }
-
- private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
+ private fun create(scope: CoroutineScope): UserRepositoryImpl {
return UserRepositoryImpl(
appContext = context,
manager = manager,
applicationScope = scope,
- mainDispatcher = IMMEDIATE,
- backgroundDispatcher = IMMEDIATE,
+ mainDispatcher = testDispatcher,
+ backgroundDispatcher = testDispatcher,
globalSettings = globalSettings,
tracker = tracker,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 88b2630..a0cfab4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -23,12 +23,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,9 +41,10 @@
@RunWith(AndroidJUnit4::class)
class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
- private val secureSettings = FakeSettings()
+ private val kosmos = testKosmos()
+ private val dispatcher = kosmos.testDispatcher
+ private val testScope = kosmos.testScope
+ private val secureSettings = kosmos.fakeSettings
private val userRepository = Kosmos().fakeUserRepository
private lateinit var repository: UserAwareSecureSettingsRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
index 86a8ae5..1ef3464 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
@@ -17,11 +17,8 @@
package com.android.systemui.common.ui
import android.content.applicationContext
-import android.view.layoutInflater
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.policy.configurationController
val Kosmos.configurationState: ConfigurationState by
- Kosmos.Fixture {
- ConfigurationState(configurationController, applicationContext, layoutInflater)
- }
+ Kosmos.Fixture { ConfigurationStateImpl(configurationController, applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index caa6e99..13116e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -34,5 +35,6 @@
sceneInteractor = sceneInteractor,
deviceUnlockedInteractor = deviceUnlockedInteractor,
alternateBouncerInteractor = alternateBouncerInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index b34681a..f8df707 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -5,9 +5,12 @@
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
+var Kosmos.unconfinedTestDispatcher by Fixture { UnconfinedTestDispatcher() }
var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
+var Kosmos.unconfinedTestScope by Fixture { TestScope(unconfinedTestDispatcher) }
var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope }
var Kosmos.testCase: SysuiTestCase by Fixture()
var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
index 41ca2f9..8fc40e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -18,11 +18,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.quickSettingsShadeOverlayActionsViewModel:
QuickSettingsShadeOverlayActionsViewModel by Fixture {
- QuickSettingsShadeOverlayActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
+ QuickSettingsShadeOverlayActionsViewModel()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index 9025c5c..ff8b478 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -17,13 +17,13 @@
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModelFactory
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayContentViewModel by
Kosmos.Fixture {
QuickSettingsShadeOverlayContentViewModel(
- overlayShadeViewModelFactory = overlayShadeViewModelFactory,
+ sceneInteractor = sceneInteractor,
shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
index 5d70ed6..128a7fc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
@@ -17,12 +17,10 @@
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
Kosmos.Fixture {
QuickSettingsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
index 5ad5cb2..cd1704c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
@@ -14,18 +14,13 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModelFactory
-import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.quickSettingsShadeSceneContentViewModel: QuickSettingsShadeSceneContentViewModel by
Kosmos.Fixture {
QuickSettingsShadeSceneContentViewModel(
- overlayShadeViewModelFactory = overlayShadeViewModelFactory,
quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index dc45d93..b3664e1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -2,7 +2,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.scene.shared.model.FakeScene
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
@@ -19,10 +18,6 @@
)
}
-val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet() }
-
-val Kosmos.scenes by Fixture { fakeScenes }
-
val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
var Kosmos.overlayKeys by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
index 59f2b94..c95b2dc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
@@ -29,7 +29,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
private val mutableTransitionState =
MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(Scenes.Lockscreen))
@@ -116,13 +115,13 @@
is ObservableTransitionState.Transition -> {
TransitionStep(
from =
- if (sceneTransition.fromScene != Scenes.Lockscreen) {
+ if (sceneTransition.fromContent != Scenes.Lockscreen) {
KeyguardState.UNDEFINED
} else {
state.from
},
to =
- if (sceneTransition.toScene != Scenes.Lockscreen) {
+ if (sceneTransition.toContent != Scenes.Lockscreen) {
KeyguardState.UNDEFINED
} else {
state.from
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
deleted file mode 100644
index 78358f5..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
+++ /dev/null
@@ -1,49 +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.scene.shared.model
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.receiveAsFlow
-
-class FakeScene(
- override val key: SceneKey,
-) : ExclusiveActivatable(), Scene {
- var isDestinationScenesBeingCollected = false
-
- private val destinationScenesChannel = Channel<Map<UserAction, UserActionResult>>()
-
- override val destinationScenes =
- destinationScenesChannel
- .receiveAsFlow()
- .onStart { isDestinationScenesBeingCollected = true }
- .onCompletion { isDestinationScenesBeingCollected = false }
-
- override suspend fun onActivated(): Nothing {
- awaitCancellation()
- }
-
- suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) {
- destinationScenesChannel.send(value)
- }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 4660337..4a86fd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -72,10 +72,6 @@
@Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
override val legacyLockscreenShadeTracking = MutableStateFlow(false)
- private var _isDualShadeAlignedToBottom = false
- override val isDualShadeAlignedToBottom
- get() = _isDualShadeAlignedToBottom
-
private var _isShadeLayoutWide = MutableStateFlow(false)
override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
@@ -155,10 +151,6 @@
_legacyShadeExpansion.value = expandedFraction
}
- fun setDualShadeAlignedToBottom(isAlignedToBottom: Boolean) {
- _isDualShadeAlignedToBottom = isAlignedToBottom
- }
-
override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
_isShadeLayoutWide.value = isShadeLayoutWide
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
index 24481bf..1e00ac4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
@@ -19,11 +19,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.notificationsShadeOverlayActionsViewModel:
NotificationsShadeOverlayActionsViewModel by Fixture {
- NotificationsShadeOverlayActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
+ NotificationsShadeOverlayActionsViewModel()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
new file mode 100644
index 0000000..9cdd519
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
+
+val Kosmos.notificationsShadeOverlayContentViewModel:
+ NotificationsShadeOverlayContentViewModel by Fixture {
+ NotificationsShadeOverlayContentViewModel(
+ shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
+ notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
+ sceneInteractor = sceneInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
index 9bf4756..f792ab9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
@@ -19,11 +19,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.notificationsShadeSceneActionsViewModel:
- NotificationsShadeSceneActionsViewModel by Fixture {
- NotificationsShadeSceneActionsViewModel(
- shadeInteractor = shadeInteractor,
- )
-}
+ NotificationsShadeSceneActionsViewModel by Fixture { NotificationsShadeSceneActionsViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
deleted file mode 100644
index 00f1526..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ /dev/null
@@ -1,38 +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.shade.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
- Kosmos.Fixture {
- OverlayShadeViewModel(
- sceneInteractor = sceneInteractor,
- shadeInteractor = shadeInteractor,
- )
- }
-
-val Kosmos.overlayShadeViewModelFactory: OverlayShadeViewModel.Factory by
- Kosmos.Fixture {
- object : OverlayShadeViewModel.Factory {
- override fun create(): OverlayShadeViewModel {
- return overlayShadeViewModel
- }
- }
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 634354b..8bfc390 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -35,3 +35,11 @@
dumpManager = dumpManager,
)
}
+
+val Kosmos.notificationsPlaceholderViewModelFactory by Fixture {
+ object : NotificationsPlaceholderViewModel.Factory {
+ override fun create(): NotificationsPlaceholderViewModel {
+ return notificationsPlaceholderViewModel
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index 35fa2af..73d423c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -19,5 +19,10 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
+
+val Kosmos.unconfinedDispatcherFakeGlobalSettings: FakeGlobalSettings by Fixture {
+ FakeGlobalSettings(unconfinedTestDispatcher)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index 76ef202..e1daf9b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -19,8 +19,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
import com.android.systemui.settings.userTracker
val Kosmos.fakeSettings: FakeSettings by Fixture {
FakeSettings(testDispatcher) { userTracker.userId }
}
+
+val Kosmos.unconfinedDispatcherFakeSettings: FakeSettings by Fixture {
+ FakeSettings(unconfinedTestDispatcher) { userTracker.userId }
+}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 08cc9c3..ffa4841 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -117,7 +117,7 @@
name: "enable_magnification_follows_mouse"
namespace: "accessibility"
description: "Whether to enable mouse following for fullscreen magnification"
- bug: "335494097"
+ bug: "354696546"
}
flag {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f1a8b5a..b541345 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4903,40 +4903,26 @@
}
@Override
- @RequiresNoPermission
- public boolean startFlashNotificationSequence(String opPkg,
- @FlashNotificationReason int reason, IBinder token) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.startFlashNotificationSequence(opPkg,
- reason, token);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
+ public boolean startFlashNotificationSequence(String opPkg, @FlashNotificationReason int reason,
+ IBinder token) {
+ startFlashNotificationSequence_enforcePermission();
+ return mFlashNotificationsController.startFlashNotificationSequence(opPkg, reason, token);
}
@Override
- @RequiresNoPermission
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
public boolean stopFlashNotificationSequence(String opPkg) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ stopFlashNotificationSequence_enforcePermission();
+ return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
}
@Override
- @RequiresNoPermission
- public boolean startFlashNotificationEvent(String opPkg,
- @FlashNotificationReason int reason, String reasonPkg) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mFlashNotificationsController.startFlashNotificationEvent(opPkg,
- reason, reasonPkg);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @EnforcePermission(MANAGE_ACCESSIBILITY)
+ public boolean startFlashNotificationEvent(String opPkg, @FlashNotificationReason int reason,
+ String reasonPkg) {
+ startFlashNotificationEvent_enforcePermission();
+ return mFlashNotificationsController.startFlashNotificationEvent(opPkg, reason, reasonPkg);
}
@Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
index c01fe31..5dd4c25 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
@@ -38,7 +38,9 @@
import java.util.concurrent.Executor;
/**
- * Helper class for interacting with a system server local appsearch session synchronously.
+ * Helper class for interacting with a system server local appsearch session asynchronously.
+ *
+ * <p>Converts the AppSearch Callback API to {@link AndroidFuture}.
*/
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public class SyncAppSearchCallHelper implements Closeable {
@@ -47,9 +49,10 @@
private final AppSearchManager mAppSearchManager;
private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
- public SyncAppSearchCallHelper(@NonNull AppSearchManager appSearchManager,
- @NonNull Executor executor,
- @NonNull SearchContext appSearchContext) {
+ public SyncAppSearchCallHelper(
+ @NonNull AppSearchManager appSearchManager,
+ @NonNull Executor executor,
+ @NonNull SearchContext appSearchContext) {
Objects.requireNonNull(appSearchManager);
Objects.requireNonNull(executor);
Objects.requireNonNull(appSearchContext);
@@ -61,68 +64,81 @@
appSearchContext, mExecutor, mSettableSessionFuture::complete);
}
- /**
- * Converts a failed app search result codes into an exception.
- */
+ /** Converts a failed app search result codes into an exception. */
@NonNull
private static Exception failedResultToException(@NonNull AppSearchResult appSearchResult) {
return switch (appSearchResult.getResultCode()) {
- case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_IO_ERROR -> new IOException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
- appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_INVALID_ARGUMENT ->
+ new IllegalArgumentException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR ->
+ new IOException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR ->
+ new SecurityException(appSearchResult.getErrorMessage());
default -> new IllegalStateException(appSearchResult.getErrorMessage());
};
}
- private AppSearchSession getSession() throws Exception {
- AppSearchResult<AppSearchSession> sessionResult = mSettableSessionFuture.get();
- if (!sessionResult.isSuccess()) {
- throw failedResultToException(sessionResult);
- }
- return sessionResult.getResultValue();
+ private AndroidFuture<AppSearchSession> getSessionAsync() {
+ return mSettableSessionFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(failedResultToException(result));
+ }
+ });
}
- /**
- * Gets the schema for a given app search session.
- */
- @WorkerThread
- public GetSchemaResponse getSchema() throws Exception {
- AndroidFuture<AppSearchResult<GetSchemaResponse>> settableSchemaResponse =
- new AndroidFuture<>();
- getSession().getSchema(mExecutor, settableSchemaResponse::complete);
- AppSearchResult<GetSchemaResponse> schemaResponse = settableSchemaResponse.get();
- if (schemaResponse.isSuccess()) {
- return schemaResponse.getResultValue();
- } else {
- throw failedResultToException(schemaResponse);
- }
+ /** Gets the schema for a given app search session. */
+ public AndroidFuture<GetSchemaResponse> getSchema() {
+ return getSessionAsync()
+ .thenComposeAsync(
+ session -> {
+ AndroidFuture<AppSearchResult<GetSchemaResponse>>
+ settableSchemaResponse = new AndroidFuture<>();
+ session.getSchema(mExecutor, settableSchemaResponse::complete);
+ return settableSchemaResponse.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ failedResultToException(result));
+ }
+ });
+ },
+ mExecutor);
}
- /**
- * Sets the schema for a given app search session.
- */
- @WorkerThread
- public SetSchemaResponse setSchema(
- @NonNull SetSchemaRequest setSchemaRequest) throws Exception {
- AndroidFuture<AppSearchResult<SetSchemaResponse>> settableSchemaResponse =
- new AndroidFuture<>();
- getSession().setSchema(
- setSchemaRequest, mExecutor, mExecutor, settableSchemaResponse::complete);
- AppSearchResult<SetSchemaResponse> schemaResponse = settableSchemaResponse.get();
- if (schemaResponse.isSuccess()) {
- return schemaResponse.getResultValue();
- } else {
- throw failedResultToException(schemaResponse);
- }
+ /** Sets the schema for a given app search session. */
+ public AndroidFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest setSchemaRequest) {
+ return getSessionAsync()
+ .thenComposeAsync(
+ session -> {
+ AndroidFuture<AppSearchResult<SetSchemaResponse>>
+ settableSchemaResponse = new AndroidFuture<>();
+ session.setSchema(
+ setSchemaRequest,
+ mExecutor,
+ mExecutor,
+ settableSchemaResponse::complete);
+ return settableSchemaResponse.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ failedResultToException(result));
+ }
+ });
+ },
+ mExecutor);
}
@Override
public void close() throws IOException {
try {
- getSession().close();
+ getSessionAsync().get().close();
} catch (Exception ex) {
Slog.e(TAG, "Failed to close app search session", ex);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 14a3211..dc0b4b8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -48,6 +48,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
import android.widget.BaseAdapter;
@@ -82,7 +83,6 @@
com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill;
private static final int THEME_ID_DARK =
com.android.internal.R.style.Theme_DeviceDefault_Autofill;
- private static final int AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS = 5;
private static final TypedValue sTempTypedValue = new TypedValue();
@@ -113,9 +113,11 @@
private final @NonNull Callback mCallback;
+ private final @NonNull WindowManager mWindowManager;
+
private final @Nullable View mHeader;
private final @NonNull ListView mListView;
- private final @Nullable View mFooter;
+ private @Nullable View mFooter;
private final @Nullable ItemsAdapter mAdapter;
@@ -134,6 +136,8 @@
private int mMaxInputLengthForAutofill;
+ private final boolean mIsCredmanAutofillSession;
+
public static boolean isFullScreen(Context context) {
if (sFullScreenMode != null) {
if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode);
@@ -158,6 +162,9 @@
mContext = new ContextThemeWrapper(context, mThemeId);
mUserContext = Helper.getUserContext(mContext);
mMaxInputLengthForAutofill = maxInputLengthForAutofill;
+ mIsCredmanAutofillSession = (Flags.autofillCredmanIntegration()
+ && ((response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0));
+ mWindowManager = mContext.getSystemService(WindowManager.class);
final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -167,7 +174,8 @@
final ViewGroup decor;
if (mFullScreen) {
decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
- } else if (headerPresentation != null || footerPresentation != null) {
+ } else if (headerPresentation != null
+ || footerPresentation != null || mIsCredmanAutofillSession) {
decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
null);
} else {
@@ -219,11 +227,7 @@
if (sVerbose) {
Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
}
- } else if (Flags.autofillCredmanIntegration() && (
- (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0)) {
- mVisibleDatasetsMaxCount = AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS;
- }
- else {
+ } else {
mVisibleDatasetsMaxCount = mContext.getResources()
.getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
}
@@ -301,7 +305,7 @@
mHeader = null;
}
- if (footerPresentation != null) {
+ if (footerPresentation != null && !mIsCredmanAutofillSession) {
final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer);
if (footerContainer != null) {
@@ -366,7 +370,22 @@
}
applyCancelAction(view, response.getCancelIds());
- items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
+ if (AutofillManager.PINNED_DATASET_ID.equals(dataset.getId())
+ && mIsCredmanAutofillSession && !items.isEmpty()) {
+ final LinearLayout footerContainer =
+ decor.findViewById(R.id.autofill_dataset_footer);
+ if (sVerbose) {
+ Slog.v(TAG, "adding footer");
+ }
+ mFooter = view;
+ footerContainer.addView(mFooter);
+ footerContainer.setVisibility(View.VISIBLE);
+ footerContainer.setClickable(true);
+ footerContainer.setOnClickListener(v -> mCallback.onDatasetPicked(dataset));
+ } else {
+ items.add(
+ new ViewItem(dataset, filterPattern, filterable, valueText, view));
+ }
}
}
@@ -459,12 +478,9 @@
if (updateContentSize()) {
requestShowFillUi();
}
- if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
- mListView.setVerticalScrollBarEnabled(true);
- mListView.onVisibilityAggregated(true);
- } else {
- mListView.setVerticalScrollBarEnabled(false);
- }
+ mListView.setVerticalScrollBarEnabled(true);
+ mListView.onVisibilityAggregated(true);
+
if (mAdapter.getCount() != oldCount) {
mListView.requestLayout();
}
@@ -578,11 +594,18 @@
return changed;
}
+ private boolean heightLesserThanDisplayScreen(int height) {
+ // Don't update list height for credential options beyond 80% of display window even if we
+ // are still under the max visible number of datasets. This could happen when font or
+ // display size is set to large.
+ return height < (0.8 * mWindowManager.getCurrentWindowMetrics().getBounds().height());
+ }
+
private boolean updateHeight(View view, Point maxSize) {
boolean changed = false;
final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
final int newContentHeight = mContentHeight + clampedMeasuredHeight;
- if (newContentHeight != mContentHeight) {
+ if (newContentHeight != mContentHeight && heightLesserThanDisplayScreen(newContentHeight)) {
mContentHeight = newContentHeight;
changed = true;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 2119622..4b9065b 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -27,7 +27,6 @@
import android.annotation.UserIdInt;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.AttributionSource;
@@ -61,6 +60,9 @@
private static final String TAG = "GenericWindowPolicyController";
+ private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
+ new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+
/** Interface to listen running applications change on virtual display. */
public interface RunningAppsChangedListener {
/**
@@ -69,29 +71,25 @@
void onRunningAppsChanged(ArraySet<Integer> runningUids);
}
- /**
- * For communicating when activities are blocked from running on the display by this policy
- * controller.
- */
- public interface ActivityBlockedCallback {
+ /** Interface to react to activity changes on the virtual display. */
+ public interface ActivityListener {
+
+ /** Called when the top activity changes. */
+ void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+ @UserIdInt int userId);
+
+ /** Called when the display becomes empty. */
+ void onDisplayEmpty(int displayId);
+
/** Called when an activity is blocked.*/
- void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender);
- }
- private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
- new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+ void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+ @Nullable IntentSender intentSender);
- /**
- * For communicating when a secure window shows on the virtual display.
- */
- public interface SecureWindowCallback {
/** Called when a secure window shows on the virtual display. */
- void onSecureWindowShown(int displayId, int uid);
- }
+ void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo);
- /** Interface to listen for interception of intents. */
- public interface IntentListenerCallback {
/** Returns true when an intent should be intercepted */
- boolean shouldInterceptIntent(Intent intent);
+ boolean shouldInterceptIntent(@NonNull Intent intent);
}
/**
@@ -118,7 +116,6 @@
private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
@NonNull
private final Object mGenericWindowPolicyControllerLock = new Object();
- @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
// Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId()
// and waitAndGetIsMirrorDisplay()
@@ -129,14 +126,12 @@
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<Integer> mRunningUids = new ArraySet<>();
- @Nullable private final ActivityListener mActivityListener;
- @Nullable private final IntentListenerCallback mIntentListenerCallback;
+ @NonNull private final ActivityListener mActivityListener;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
new ArraySet<>();
- @Nullable private final SecureWindowCallback mSecureWindowCallback;
@NonNull private final Set<String> mDisplayCategories;
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -162,12 +157,6 @@
* @param crossTaskNavigationExemptions The set of components explicitly exempt from the default
* navigation policy.
* @param activityListener Activity listener to listen for activity changes.
- * @param activityBlockedCallback Callback that is called when an activity is blocked from
- * launching.
- * @param secureWindowCallback Callback that is called when a secure window shows on the
- * virtual display.
- * @param intentListenerCallback Callback that is called to intercept intents when matching
- * passed in filters.
* @param showTasksInHostDeviceRecents whether to show activities in recents on the host device.
* @param customHomeComponent The component acting as a home activity on the virtual display. If
* {@code null}, then the system-default secondary home activity will be used. This is only
@@ -184,10 +173,7 @@
@NonNull Set<String> activityPolicyPackageExemptions,
boolean crossTaskNavigationAllowedByDefault,
@NonNull Set<ComponentName> crossTaskNavigationExemptions,
- @Nullable ActivityListener activityListener,
- @Nullable ActivityBlockedCallback activityBlockedCallback,
- @Nullable SecureWindowCallback secureWindowCallback,
- @Nullable IntentListenerCallback intentListenerCallback,
+ @NonNull ActivityListener activityListener,
@NonNull Set<String> displayCategories,
boolean showTasksInHostDeviceRecents,
@Nullable ComponentName customHomeComponent) {
@@ -199,11 +185,8 @@
mActivityPolicyPackageExemptions = new ArraySet<>(activityPolicyPackageExemptions);
mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
- mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
- mSecureWindowCallback = secureWindowCallback;
- mIntentListenerCallback = intentListenerCallback;
mDisplayCategories = displayCategories;
mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents;
mCustomHomeComponent = customHomeComponent;
@@ -306,8 +289,7 @@
@Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
int launchingFromDisplayId, boolean isNewTask, boolean isResultExpected,
@Nullable Supplier<IntentSender> intentSender) {
- if (mIntentListenerCallback != null && intent != null
- && mIntentListenerCallback.shouldInterceptIntent(intent)) {
+ if (intent != null && mActivityListener.shouldInterceptIntent(intent)) {
logActivityLaunchBlocked("Virtual device intercepting intent");
return false;
}
@@ -391,11 +373,9 @@
int displayId = waitAndGetDisplayId();
// The callback is fired only when windowFlags are changed. To let VirtualDevice owner
// aware that the virtual display has a secure window on top.
- if ((windowFlags & FLAG_SECURE) != 0 && mSecureWindowCallback != null
- && displayId != INVALID_DISPLAY) {
+ if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) {
// Post callback on the main thread, so it doesn't block activity launching.
- mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId,
- activityInfo.applicationInfo.uid));
+ mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
}
if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
@@ -418,7 +398,7 @@
// Don't send onTopActivityChanged() callback when topActivity is null because it's defined
// as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when
// there is no activity running on virtual display.
- if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) {
+ if (topActivity != null && displayId != INVALID_DISPLAY) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() ->
mActivityListener.onTopActivityChanged(displayId, topActivity, userId));
@@ -431,8 +411,7 @@
mRunningUids.clear();
mRunningUids.addAll(runningUids);
int displayId = waitAndGetDisplayId();
- if (mActivityListener != null && mRunningUids.isEmpty()
- && displayId != INVALID_DISPLAY) {
+ if (mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId));
}
@@ -482,9 +461,8 @@
int displayId = waitAndGetDisplayId();
// Don't trigger activity blocked callback for mirror displays, because we can't show
// any activity or presentation on it anyway.
- if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null
- && displayId != INVALID_DISPLAY) {
- mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo,
+ if (!waitAndGetIsMirrorDisplay() && displayId != INVALID_DISPLAY) {
+ mActivityListener.onActivityLaunchBlocked(displayId, activityInfo,
intentSender == null ? null : intentSender.get());
}
Counter.logIncrementWithUid(
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 4eb50a9..cd2dd3a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -40,6 +40,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.virtual.ActivityPolicyExemption;
import android.companion.virtual.IVirtualDevice;
@@ -48,7 +49,6 @@
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -56,6 +56,8 @@
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -88,6 +90,7 @@
import android.media.AudioManager;
import android.media.audiopolicy.AudioMix;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -132,6 +135,16 @@
private static final String TAG = "VirtualDeviceImpl";
+ /**
+ * Do not show a toast on the virtual display when a secure surface is shown after
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. VDM clients should use
+ * {@link VirtualDeviceManager.ActivityListener#onSecureWindowShown} instead to provide
+ * a custom notification if desired.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN = 311101667L;
+
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
| DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
@@ -182,7 +195,7 @@
@GuardedBy("mVirtualDeviceLock")
private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
private IVirtualDeviceActivityListener mActivityListener;
- private ActivityListener mActivityListenerAdapter = null;
+ private GenericWindowPolicyController.ActivityListener mActivityListenerAdapter = null;
private IVirtualDeviceSoundEffectListener mSoundEffectListener;
private final DisplayManagerGlobal mDisplayManager;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -207,50 +220,122 @@
@NonNull
private final Set<String> mActivityPolicyPackageExemptions = new ArraySet<>();
- private ActivityListener createListenerAdapter() {
- return new ActivityListener() {
+ private class GwpcActivityListener implements GenericWindowPolicyController.ActivityListener {
- @Override
- public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity) {
- try {
- mActivityListener.onTopActivityChanged(displayId, topActivity,
- UserHandle.USER_NULL);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
+ @Override
+ public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+ @UserIdInt int userId) {
+ try {
+ mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+
+ @Override
+ public void onDisplayEmpty(int displayId) {
+ try {
+ mActivityListener.onDisplayEmpty(displayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+
+ @Override
+ public void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+ @Nullable IntentSender intentSender) {
+ Intent intent =
+ BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
+ if (shouldShowBlockedActivityDialog(
+ activityInfo.getComponentName(), intent.getComponent())) {
+ mContext.startActivityAsUser(
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
+ ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
+ UserHandle.SYSTEM);
}
- @Override
- public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
- @UserIdInt int userId) {
- try {
- mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
- }
-
- @Override
- public void onDisplayEmpty(int displayId) {
- try {
- mActivityListener.onDisplayEmpty(displayId);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
- }
- }
-
- @Override
- public void onActivityLaunchBlocked(int displayId,
- @NonNull ComponentName componentName, @NonNull UserHandle user,
- @Nullable IntentSender intentSender) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
try {
mActivityListener.onActivityLaunchBlocked(
- displayId, componentName, user, intentSender);
+ displayId,
+ activityInfo.getComponentName(),
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid),
+ intentSender);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
}
}
- };
+ }
+
+ @Override
+ public void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+ try {
+ mActivityListener.onSecureWindowShown(
+ displayId,
+ activityInfo.getComponentName(),
+ UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+
+ if (CompatChanges.isChangeEnabled(DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN,
+ mOwnerPackageName, UserHandle.getUserHandleForUid(mOwnerUid))) {
+ return;
+ }
+ }
+
+ // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
+ // if the secure window is shown on a non-secure virtual display.
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(displayId);
+ if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
+ showToastWhereUidIsRunning(activityInfo.applicationInfo.uid,
+ com.android.internal.R.string.vdm_secure_window,
+ Toast.LENGTH_LONG, mContext.getMainLooper());
+
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_secure_window_blocked_count",
+ mAttributionSource.getUid());
+ }
+ }
+
+ /**
+ * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true
+ * if the intent matches any filter notifying the DisplayPolicyController to abort the
+ * activity launch to be replaced by the interception.
+ */
+ @Override
+ public boolean shouldInterceptIntent(@NonNull Intent intent) {
+ synchronized (mVirtualDeviceLock) {
+ boolean hasInterceptedIntent = false;
+ for (Map.Entry<IBinder, IntentFilter> interceptor
+ : mIntentInterceptors.entrySet()) {
+ IntentFilter intentFilter = interceptor.getValue();
+ // Explicitly match the actions because the intent filter will match any intent
+ // without an explicit action. If the intent has no action, then require that
+ // there are no actions specified in the filter either.
+ boolean explicitActionMatch =
+ intent.getAction() != null || intentFilter.countActions() == 0;
+ if (explicitActionMatch && intentFilter.match(
+ intent.getAction(), intent.getType(), intent.getScheme(),
+ intent.getData(), intent.getCategories(), TAG) >= 0) {
+ try {
+ // For privacy reasons, only returning the intents action and data.
+ // Any other required field will require a review.
+ IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
+ .onIntentIntercepted(
+ new Intent(intent.getAction(), intent.getData()));
+ hasInterceptedIntent = true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener", e);
+ }
+ }
+ }
+ return hasInterceptedIntent;
+ }
+ }
}
VirtualDeviceImpl(
@@ -1290,7 +1375,7 @@
Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
if (mActivityListenerAdapter == null) {
- mActivityListenerAdapter = createListenerAdapter();
+ mActivityListenerAdapter = new GwpcActivityListener();
}
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
@@ -1306,9 +1391,6 @@
? mParams.getBlockedCrossTaskNavigations()
: mParams.getAllowedCrossTaskNavigations(),
mActivityListenerAdapter,
- this::onActivityBlocked,
- this::onSecureWindowShown,
- this::shouldInterceptIntent,
displayCategories,
showTasksInHostDeviceRecents,
homeComponent);
@@ -1378,28 +1460,6 @@
}
}
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
- private void onActivityBlocked(int displayId, ActivityInfo activityInfo,
- IntentSender intentSender) {
- Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
- if (shouldShowBlockedActivityDialog(
- activityInfo.getComponentName(), intent.getComponent())) {
- mContext.startActivityAsUser(
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
- ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
- UserHandle.SYSTEM);
- }
-
- if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
- mActivityListenerAdapter.onActivityLaunchBlocked(
- displayId,
- activityInfo.getComponentName(),
- UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid),
- intentSender);
- }
- }
-
private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent,
ComponentName blockedAppStreamingActivityComponent) {
if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) {
@@ -1414,27 +1474,6 @@
return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY) == DEVICE_POLICY_DEFAULT;
}
- private void onSecureWindowShown(int displayId, int uid) {
- synchronized (mVirtualDeviceLock) {
- if (!mVirtualDisplays.contains(displayId)) {
- return;
- }
- }
-
- // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
- // if the secure window is shown on a non-secure virtual display.
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(displayId);
- if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
- showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
- Toast.LENGTH_LONG, mContext.getMainLooper());
-
- Counter.logIncrementWithUid(
- "virtual_devices.value_secure_window_blocked_count",
- mAttributionSource.getUid());
- }
- }
-
private ArraySet<UserHandle> getAllowedUserHandles() {
ArraySet<UserHandle> result = new ArraySet<>();
final long token = Binder.clearCallingIdentity();
@@ -1621,40 +1660,6 @@
}
}
- /**
- * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
- * the intent matches any filter notifying the DisplayPolicyController to abort the
- * activity launch to be replaced by the interception.
- */
- private boolean shouldInterceptIntent(Intent intent) {
- synchronized (mVirtualDeviceLock) {
- boolean hasInterceptedIntent = false;
- for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
- IntentFilter intentFilter = interceptor.getValue();
- // Explicitly match the actions because the intent filter will match any intent
- // without an explicit action. If the intent has no action, then require that there
- // are no actions specified in the filter either.
- boolean explicitActionMatch =
- intent.getAction() != null || intentFilter.countActions() == 0;
- if (explicitActionMatch && intentFilter.match(
- intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
- intent.getCategories(), TAG) >= 0) {
- try {
- // For privacy reasons, only returning the intents action and data. Any
- // other required field will require a review.
- IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
- .onIntentIntercepted(new Intent(intent.getAction(), intent.getData()));
- hasInterceptedIntent = true;
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e);
- }
- }
- }
-
- return hasInterceptedIntent;
- }
- }
-
interface PendingTrampolineCallback {
/**
* Called when the callback should start waiting for the given pending trampoline.
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 07e5f2e..d86bae1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,6 +224,9 @@
/** 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<>();
@@ -3144,7 +3147,9 @@
Objects.requireNonNull(rawPath, "rawPath cannot be null");
Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
Objects.requireNonNull(token, "token cannot be null");
- Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
+ Objects.requireNonNull(obbInfo, "obbInfo cannot be null");
+
+ validateObbInfo(obbInfo, rawPath);
final int callingUid = Binder.getCallingUid();
final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -3156,6 +3161,34 @@
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/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 240e91b..907e7c6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -45,6 +45,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -562,6 +563,7 @@
public void resetShortTermModel() {
mCurrentBrightnessMapper.clearUserDataPoints();
mShortTermModel.reset();
+ Slog.i(TAG, "Resetting short term model");
}
public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -598,74 +600,79 @@
}
public void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.increaseIndent();
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
- pw.println(" mState=" + configStateToString(mState));
- pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
- pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
- pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
- pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
- pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
- pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
- pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
- pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
- pw.println(" mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
- pw.println(" mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
- pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
- pw.println(" mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
- pw.println(" mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
- pw.println(" mWeightingIntercept=" + mWeightingIntercept);
+ pw.println("----------------------------------------------");
+ ipw.println("mState=" + configStateToString(mState));
+ ipw.println("mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+ ipw.println("mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ ipw.println("mDozeScaleFactor=" + mDozeScaleFactor);
+ ipw.println("mInitialLightSensorRate=" + mInitialLightSensorRate);
+ ipw.println("mNormalLightSensorRate=" + mNormalLightSensorRate);
+ ipw.println("mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+ ipw.println("mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
+ ipw.println("mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+ ipw.println("mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+ ipw.println("mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
+ ipw.println("mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
+ ipw.println("mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+ ipw.println("mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
+ ipw.println("mWeightingIntercept=" + mWeightingIntercept);
pw.println();
pw.println("Automatic Brightness Controller State:");
- pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
- pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
- pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
- pw.println(" mAmbientLux=" + mAmbientLux);
- pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mPreThresholdLux=" + mPreThresholdLux);
- pw.println(" mPreThresholdBrightness=" + mPreThresholdBrightness);
- pw.println(" mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
- pw.println(" mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
- pw.println(" mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
- pw.println(" mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
- pw.println(" mLastObservedLux=" + mLastObservedLux);
- pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
- pw.println(" mRecentLightSamples=" + mRecentLightSamples);
- pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
- pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
- pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
- pw.println(" mShortTermModel=");
- mShortTermModel.dump(pw);
- pw.println(" mPausedShortTermModel=");
- mPausedShortTermModel.dump(pw);
+ pw.println("--------------------------------------");
+ ipw.println("mLightSensor=" + mLightSensor);
+ ipw.println("mLightSensorEnabled=" + mLightSensorEnabled);
+ ipw.println("mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ ipw.println("mCurrentLightSensorRate=" + mCurrentLightSensorRate);
+ ipw.println("mAmbientLux=" + mAmbientLux);
+ ipw.println("mAmbientLuxValid=" + mAmbientLuxValid);
+ ipw.println("mPreThresholdLux=" + mPreThresholdLux);
+ ipw.println("mPreThresholdBrightness=" + mPreThresholdBrightness);
+ ipw.println("mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
+ ipw.println("mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
+ ipw.println("mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
+ ipw.println("mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
+ ipw.println("mLastObservedLux=" + mLastObservedLux);
+ ipw.println("mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+ ipw.println("mRecentLightSamples=" + mRecentLightSamples);
+ ipw.println("mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+ ipw.println("mScreenAutoBrightness=" + mScreenAutoBrightness);
+ ipw.println("mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
+ ipw.println("mShortTermModel=");
- pw.println();
- pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
- pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
- pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mShortTermModel.dump(ipw);
+ ipw.println("mPausedShortTermModel=");
+ mPausedShortTermModel.dump(ipw);
+
+ ipw.println();
+ ipw.println("mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
+ ipw.println("mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
+ ipw.println("mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
- pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
- pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
- pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
- pw.println(" Current mode="
+ ipw.println("mForegroundAppPackageName=" + mForegroundAppPackageName);
+ ipw.println("mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ ipw.println("mForegroundAppCategory=" + mForegroundAppCategory);
+ ipw.println("mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+ ipw.println("Current mode="
+ autoBrightnessModeToString(mCurrentBrightnessMapper.getMode()));
for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
- pw.println();
- pw.println(" Mapper for mode "
+ ipw.println();
+ ipw.println("Mapper for mode "
+ autoBrightnessModeToString(mBrightnessMappingStrategyMap.keyAt(i)) + ":");
- mBrightnessMappingStrategyMap.valueAt(i).dump(pw,
+ mBrightnessMappingStrategyMap.valueAt(i).dump(ipw,
mBrightnessRangeController.getNormalBrightnessMax());
}
- pw.println();
- pw.println(" mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
- pw.println(" mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
- pw.println(" mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
- pw.println(" mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
+ ipw.println();
+ ipw.println("mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
+ ipw.println("mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
+ ipw.println("mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
+ ipw.println("mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
}
public float[] getLastSensorValues() {
@@ -1339,8 +1346,10 @@
+ "\n mIsValid: " + mIsValid;
}
- void dump(PrintWriter pw) {
- pw.println(this);
+ void dump(IndentingPrintWriter ipw) {
+ ipw.increaseIndent();
+ ipw.println(this);
+ ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b0507fb..6a019f3 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -1073,7 +1073,9 @@
pw.println(" mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
pw.println(" shortTermModelTimeout=" + getShortTermModelTimeout());
- pw.println(" Previous short-term models (oldest to newest): ");
+ if (!mPreviousBrightnessSplines.isEmpty()) {
+ pw.println(" Previous short-term models (oldest to newest): ");
+ }
for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) {
pw.println(" Computed at "
+ FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": ");
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index 631e751..b56a234 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -265,6 +265,7 @@
private void dumpLocal(PrintWriter pw) {
pw.println("BrightnessThrottler:");
+ pw.println("--------------------");
pw.println(" mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println(" mThermalThrottlingData=" + mThermalThrottlingData);
pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index ac5dd20..0f65360 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -782,6 +782,7 @@
public void dump(final PrintWriter pw) {
pw.println("BrightnessTracker state:");
+ pw.println("------------------------");
synchronized (mDataCollectionLock) {
pw.println(" mStarted=" + mStarted);
pw.println(" mLightSensor=" + mLightSensor);
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 146810f..4c133ff 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -95,6 +95,7 @@
public void dumpLocked(IndentingPrintWriter ipw) {
ipw.println("DeviceStateToLayoutMap:");
+ ipw.println("-----------------------");
ipw.increaseIndent();
ipw.println("mIsPortInDisplayLayoutEnabled=" + mIsPortInDisplayLayoutEnabled);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index cc115f1..d78fdfa 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -150,7 +150,9 @@
* <screenBrightnessDefault>0.65</screenBrightnessDefault>
* <powerThrottlingConfig>
* <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
- * <pollingWindowMillis>15</pollingWindowMillis>
+ * <customAnimationRateSec>0.004</customAnimationRateSec>
+ * <pollingWindowMaxMillis>30000</pollingWindowMaxMillis>
+ * <pollingWindowMinMillis>10000</pollingWindowMinMillis>
* <powerThrottlingMap>
* <powerThrottlingPoint>
* <thermalStatus>severe</thermalStatus>
@@ -2184,9 +2186,13 @@
return;
}
float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
- int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
+ float customAnimationRateSec = powerThrottlingCfg.getCustomAnimationRateSec().floatValue();
+ int pollingWindowMaxMillis = powerThrottlingCfg.getPollingWindowMaxMillis().intValue();
+ int pollingWindowMinMillis = powerThrottlingCfg.getPollingWindowMinMillis().intValue();
mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
- pollingWindowMillis);
+ customAnimationRateSec,
+ pollingWindowMaxMillis,
+ pollingWindowMinMillis);
}
private void loadRefreshRateSetting(DisplayConfiguration config) {
@@ -2980,12 +2986,19 @@
public static class PowerThrottlingConfigData {
/** Lowest brightness cap allowed for this device. */
public final float brightnessLowestCapAllowed;
- /** Time window for polling power in seconds. */
- public final int pollingWindowMillis;
+ /** Time take to animate brightness in seconds. */
+ public final float customAnimationRateSec;
+ /** Time window for maximum polling power in milliseconds. */
+ public final int pollingWindowMaxMillis;
+ /** Time window for minimum polling power in milliseconds. */
+ public final int pollingWindowMinMillis;
public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
- int pollingWindowMillis) {
+ float customAnimationRateSec, int pollingWindowMaxMillis,
+ int pollingWindowMinMillis) {
this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
- this.pollingWindowMillis = pollingWindowMillis;
+ this.customAnimationRateSec = customAnimationRateSec;
+ this.pollingWindowMaxMillis = pollingWindowMaxMillis;
+ this.pollingWindowMinMillis = pollingWindowMinMillis;
}
@Override
@@ -2993,7 +3006,9 @@
return "PowerThrottlingConfigData{"
+ "brightnessLowestCapAllowed: "
+ brightnessLowestCapAllowed
- + ", pollingWindowMillis: " + pollingWindowMillis
+ + ", customAnimationRateSec: " + customAnimationRateSec
+ + ", pollingWindowMaxMillis: " + pollingWindowMaxMillis
+ + ", pollingWindowMinMillis: " + pollingWindowMinMillis
+ "} ";
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 187caba..99ad65d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3340,6 +3340,7 @@
pw.println();
final int displayStateCount = mDisplayStates.size();
pw.println("Display States: size=" + displayStateCount);
+ pw.println("---------------------");
for (int i = 0; i < displayStateCount; i++) {
final int displayId = mDisplayStates.keyAt(i);
final int displayState = mDisplayStates.valueAt(i);
@@ -3355,6 +3356,7 @@
pw.println();
pw.println("Display Adapters: size=" + mDisplayAdapters.size());
+ pw.println("------------------------");
for (DisplayAdapter adapter : mDisplayAdapters) {
pw.println(" " + adapter.getName());
adapter.dumpLocked(ipw);
@@ -3362,6 +3364,7 @@
pw.println();
pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
+ pw.println("-----------------------");
mDisplayDeviceRepo.forEachLocked(device -> {
pw.println(" " + device.getDisplayDeviceInfoLocked());
device.dumpLocked(ipw);
@@ -3373,6 +3376,7 @@
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
+ pw.println("-----------------");
for (int i = 0; i < callbackCount; i++) {
CallbackRecord callback = mCallbacks.valueAt(i);
pw.println(" " + i + ": mPid=" + callback.mPid
@@ -3385,6 +3389,7 @@
for (int i = 0; i < displayPowerControllerCount; i++) {
mDisplayPowerControllers.valueAt(i).dump(pw);
}
+
pw.println();
mPersistentDataStore.dump(pw);
@@ -3403,8 +3408,10 @@
}
pw.println();
mDisplayModeDirector.dump(pw);
+ pw.println();
mBrightnessSynchronizer.dump(pw);
if (mSmallAreaDetectionController != null) {
+ pw.println();
mSmallAreaDetectionController.dump(pw);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 047eb29..bf559c1 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -253,10 +253,10 @@
// The display blanker.
private final DisplayBlanker mBlanker;
- // The LogicalDisplay tied to this DisplayPowerController2.
+ // The LogicalDisplay tied to this DisplayPowerController.
private final LogicalDisplay mLogicalDisplay;
- // The ID of the LogicalDisplay tied to this DisplayPowerController2.
+ // The ID of the LogicalDisplay tied to this DisplayPowerController.
private final int mDisplayId;
// The ID of the display which this display follows for brightness purposes.
@@ -466,7 +466,7 @@
private ObjectAnimator mColorFadeOffAnimator;
private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- // True if this DisplayPowerController2 has been stopped and should no longer be running.
+ // True if this DisplayPowerController has been stopped and should no longer be running.
private boolean mStopped;
private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -591,7 +591,8 @@
mThermalBrightnessThrottlingDataId,
logicalDisplay.getPowerThrottlingDataIdLocked(),
mDisplayDeviceConfig, displayDeviceInfo.width, displayDeviceInfo.height,
- displayToken, mDisplayId), mContext, flags, mSensorManager);
+ displayToken, mDisplayId), mContext, flags, mSensorManager,
+ mDisplayBrightnessController.getCurrentBrightness());
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -861,7 +862,7 @@
mLeadDisplayId = leadDisplayId;
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
- Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
+ mLogicalDisplay.getDisplayIdLocked());
return;
}
@@ -935,7 +936,7 @@
/**
* Unregisters all listeners and interrupts all running threads; halting future work.
*
- * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
+ * This method should be called when the DisplayPowerController is no longer in use; i.e. when
* the {@link #mDisplayId display} has been removed.
*/
@Override
@@ -2628,6 +2629,8 @@
synchronized (mLock) {
pw.println();
pw.println("Display Power Controller:");
+ pw.println("-------------------------");
+
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
@@ -2702,25 +2705,39 @@
+ mColorFadeOffAnimator.isStarted());
}
+ pw.println();
if (mPowerState != null) {
mPowerState.dump(pw);
}
- if (mAutomaticBrightnessController != null) {
- mAutomaticBrightnessController.dump(pw);
- dumpBrightnessEvents(pw);
+ pw.println();
+ if (mDisplayBrightnessController != null) {
+ mDisplayBrightnessController.dump(pw);
}
- dumpRbcEvents(pw);
-
- if (mScreenOffBrightnessSensorController != null) {
- mScreenOffBrightnessSensorController.dump(pw);
+ pw.println();
+ if (mBrightnessClamperController != null) {
+ mBrightnessClamperController.dump(ipw);
}
+ pw.println();
if (mBrightnessRangeController != null) {
mBrightnessRangeController.dump(pw);
}
+ pw.println();
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ dumpBrightnessEvents(pw);
+ }
+ dumpRbcEvents(pw);
+
+ pw.println();
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.dump(pw);
+ }
+
+ pw.println();
if (mBrightnessThrottler != null) {
mBrightnessThrottler.dump(pw);
}
@@ -2732,24 +2749,13 @@
}
pw.println();
-
if (mWakelockController != null) {
mWakelockController.dumpLocal(pw);
}
pw.println();
- if (mDisplayBrightnessController != null) {
- mDisplayBrightnessController.dump(pw);
- }
-
- pw.println();
if (mDisplayStateController != null) {
- mDisplayStateController.dumpsys(pw);
- }
-
- pw.println();
- if (mBrightnessClamperController != null) {
- mBrightnessClamperController.dump(ipw);
+ mDisplayStateController.dump(pw);
}
}
@@ -3300,10 +3306,10 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
return new BrightnessClamperController(handler, clamperChangeListener, data, context,
- flags, sensorManager);
+ flags, sensorManager, currentBrightness);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index 882c02f..215932c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -315,6 +315,7 @@
public void dumpLocal(PrintWriter pw) {
pw.println();
pw.println("DisplayPowerProximityStateController:");
+ pw.println("-------------------------------------");
synchronized (mLock) {
pw.println(" mPendingWaitForNegativeProximityLocked="
+ mPendingWaitForNegativeProximityLocked);
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index e5efebc..2fbb114 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -344,7 +344,6 @@
}
public void dump(PrintWriter pw) {
- pw.println();
pw.println("Display Power State:");
pw.println(" mStopped=" + mStopped);
pw.println(" mScreenState=" + Display.stateToString(mScreenState));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 6e0b9cf..0570b2a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1286,7 +1286,10 @@
pw.println(" " + mSupportedModes.valueAt(i));
}
pw.println("mSupportedColorModes=" + mSupportedColorModes);
- pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
+ pw.println("");
+ pw.println("DisplayDeviceConfig: ");
+ pw.println("---------------------");
+ pw.println(mDisplayDeviceConfig);
}
private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5d55d190..e8be8a4 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -1040,6 +1040,9 @@
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
+ pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null
+ ? mPrimaryDisplayDevice.getNameLocked() + "(" + mPrimaryDisplayDevice.getUniqueId()
+ + ")" : "null"));
pw.println("mIsEnabled=" + mIsEnabled);
pw.println("mIsInTransition=" + mIsInTransition);
pw.println("mLayerStack=" + mLayerStack);
@@ -1049,8 +1052,6 @@
pw.println("mRequestedColorMode=" + mRequestedColorMode);
pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled);
- pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
- mPrimaryDisplayDevice.getNameLocked() : "null"));
pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e9ecfc6..c3f6a52 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -427,6 +427,7 @@
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
+ pw.println("---------------------");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
@@ -477,14 +478,14 @@
// The boot animation might still be in progress, we do not want to switch states now
// as the boot animation would end up with an incorrect size.
if (DEBUG) {
- Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
- + " until boot is completed");
+ Slog.d(TAG, "Postponing transition to state: "
+ + mPendingDeviceState.getIdentifier() + " until boot is completed");
}
mDeviceStateToBeAppliedAfterBoot = state;
return;
}
- Slog.i(TAG, "Requesting Transition to state: " + state + ", from state="
+ Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
+ mDeviceState.getIdentifier() + ", interactive=" + mInteractive
+ ", mBootCompleted=" + mBootCompleted);
// As part of a state transition, we may need to turn off some displays temporarily so that
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 2d7792d..9cdc918 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -628,7 +628,9 @@
}
public void dump(PrintWriter pw) {
- pw.println("PersistentDataStore");
+ pw.println("PersistentDataStore:");
+ pw.println("--------------------");
+
pw.println(" mLoaded=" + mLoaded);
pw.println(" mDirty=" + mDirty);
pw.println(" RememberedWifiDisplays:");
diff --git a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
index 0a884c9..b63eba3 100644
--- a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
+++ b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
@@ -121,6 +121,7 @@
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("Screen Off Brightness Sensor Controller:");
+ pw.println("----------------------------------------");
IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
idpw.increaseIndent();
idpw.println("registered=" + mRegistered);
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index bf384b0..3ed7e57 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -133,7 +133,8 @@
}
void dump(PrintWriter pw) {
- pw.println("Small area detection allowlist");
+ pw.println("Small area detection allowlist:");
+ pw.println("-------------------------------");
pw.println(" Packages:");
synchronized (mLock) {
for (String pkg : mAllowPkgMap.keySet()) {
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 5b0229c..3520723 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -417,6 +417,7 @@
*/
public void dumpLocal(PrintWriter pw) {
pw.println("WakelockController State:");
+ pw.println("-------------------------");
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mUnfinishedBusiness=" + hasUnfinishedBusiness());
pw.println(" mOnStateChangePending=" + isOnStateChangedPending());
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 1b49bbc..06890e7 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -262,6 +262,7 @@
public void dump(PrintWriter writer) {
writer.println();
writer.println("DisplayBrightnessStrategySelector:");
+ writer.println("----------------------------------");
writer.println(" mDisplayId= " + mDisplayId);
writer.println(" mOldBrightnessStrategyName= " + mOldBrightnessStrategyName);
writer.println(
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 8ee7085..d3be33f 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -75,10 +75,13 @@
private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
+ private final DisplayManagerFlags mDisplayManagerFlags;
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@Nullable
+ private BrightnessPowerClamper mPowerClamper;
+ @Nullable
private Type mClamperType = null;
private boolean mClamperApplied = false;
@@ -93,16 +96,18 @@
public BrightnessClamperController(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
- this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager);
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
+ this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager,
+ currentBrightness);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler = handler;
+ mDisplayManagerFlags = flags;
mLightSensorController = injector.getLightSensorController(sensorManager, context,
mLightSensorListener, mHandler);
@@ -117,7 +122,15 @@
};
mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
- context);
+ context, currentBrightness);
+ if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+ for (BrightnessClamper clamper: mClampers) {
+ if (clamper.getType() == Type.POWER) {
+ mPowerClamper = (BrightnessPowerClamper) clamper;
+ break;
+ }
+ }
+ }
mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
data);
@@ -183,6 +196,12 @@
mModifiers.get(i).apply(request, builder);
}
+ if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+ if (mPowerClamper != null) {
+ mPowerClamper.updateCurrentBrightness(cappedBrightness);
+ }
+ }
+
return builder.build();
}
@@ -214,6 +233,7 @@
*/
public void dump(PrintWriter writer) {
writer.println("BrightnessClamperController:");
+ writer.println("----------------------------");
writer.println(" mBrightnessCap: " + mBrightnessCap);
writer.println(" mClamperType: " + mClamperType);
writer.println(" mClamperApplied: " + mClamperApplied);
@@ -311,13 +331,17 @@
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
- DisplayManagerFlags flags, Context context) {
+ DisplayManagerFlags flags, Context context, float currentBrightness) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
clampers.add(
new BrightnessThermalClamper(handler, clamperChangeListener, data));
if (flags.isPowerThrottlingClamperEnabled()) {
- clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
- data));
+ // Check if power-throttling config is present.
+ PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
+ if (configData != null) {
+ clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
+ data, currentBrightness));
+ }
}
if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 790322d..85e81f9 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -21,16 +21,23 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Temperature;
import android.provider.DeviceConfigInterface;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -65,14 +72,21 @@
private PowerThrottlingData mPowerThrottlingDataActive = null;
@Nullable
private PowerThrottlingConfigData mPowerThrottlingConfigData = null;
-
+ @NonNull
+ private final ThermalLevelListener mThermalLevelListener;
+ @NonNull
+ private final PowerChangeListener mPowerChangeListener;
private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
+ private boolean mCurrentThermalLevelChanged = false;
private float mCurrentAvgPowerConsumed = 0;
@Nullable
private String mUniqueDisplayId = null;
@Nullable
private String mDataId = null;
-
+ private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
+ private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+ private float mCustomAnimationRateSecDeviceConfig =
+ DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -88,23 +102,41 @@
BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
- PowerData powerData) {
- this(new Injector(), handler, listener, powerData);
+ PowerData powerData, float currentBrightness) {
+ this(new Injector(), handler, listener, powerData, currentBrightness);
}
@VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
- PowerData powerData) {
+ PowerData powerData, float currentBrightness) {
super(handler, listener);
mInjector = injector;
- mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mCurrentBrightness = currentBrightness;
+ mPowerChangeListener = (powerConsumed, thermalStatus) -> {
+ recalculatePowerQuotaChange(powerConsumed, thermalStatus);
+ };
+ mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
+ if (mPowerThrottlingConfigData != null) {
+ mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+ }
+ mThermalLevelListener = new ThermalLevelListener(handler);
+ mPmicMonitor =
+ mInjector.getPmicMonitor(mPowerChangeListener,
+ mThermalLevelListener.getThermalService(),
+ mPowerThrottlingConfigData.pollingWindowMaxMillis,
+ mPowerThrottlingConfigData.pollingWindowMinMillis);
+ mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler.post(() -> {
setDisplayData(powerData);
loadOverrideData();
start();
});
+ }
+ @VisibleForTesting
+ PowerChangeListener getPowerChangeListener() {
+ return mPowerChangeListener;
}
@Override
@@ -114,6 +146,11 @@
}
@Override
+ float getCustomAnimationRate() {
+ return mCustomAnimationRateSec;
+ }
+
+ @Override
void onDeviceConfigChanged() {
mHandler.post(() -> {
loadOverrideData();
@@ -134,6 +171,9 @@
if (mPmicMonitor != null) {
mPmicMonitor.shutdown();
}
+ if (mThermalLevelListener != null) {
+ mThermalLevelListener.stop();
+ }
}
/**
@@ -144,11 +184,20 @@
pw.println(" mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
pw.println(" mCurrentThermalLevel=" + mCurrentThermalLevel);
+ pw.println(" mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged);
pw.println(" mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
: mPowerThrottlingDataFromDDC.toString()));
+ mThermalLevelListener.dump(pw);
super.dump(pw);
}
+ /**
+ * Updates current brightness, for power calculations.
+ */
+ public void updateCurrentBrightness(float currentBrightness) {
+ mCurrentBrightness = currentBrightness;
+ }
+
private void recalculateActiveData() {
if (mUniqueDisplayId == null || mDataId == null) {
return;
@@ -156,17 +205,11 @@
mPowerThrottlingDataActive = mPowerThrottlingDataOverride
.getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
mPowerThrottlingDataFromDDC);
- if (mPowerThrottlingDataActive != null) {
- if (mPmicMonitor != null) {
- mPmicMonitor.stop();
- mPmicMonitor.start();
- }
- } else {
+ if (mPowerThrottlingDataActive == null) {
if (mPmicMonitor != null) {
mPmicMonitor.stop();
}
}
- recalculateBrightnessCap();
}
private void loadOverrideData() {
@@ -198,21 +241,57 @@
if (mPowerThrottlingDataActive == null) {
return;
}
- if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
- isActive = true;
- // calculate new brightness Cap.
- // Brightness has a linear relation to power-consumed.
- targetBrightnessCap =
- (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
- // Cap to lowest allowed brightness on device.
+ if (powerQuota > 0) {
+ if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness)
+ && (mCurrentAvgPowerConsumed > powerQuota)) {
+ isActive = true;
+ // calculate new brightness Cap.
+ // Brightness has a linear relation to power-consumed.
+ targetBrightnessCap =
+ (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness;
+ } else if (mCurrentThermalLevelChanged) {
+ if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) {
+ // reset pmic and remove the power-throttling cap.
+ isActive = true;
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ mPmicMonitor.stop();
+ } else {
+ isActive = true;
+ // Since the thermal status has changed, we need to remove power-throttling cap.
+ // Instead of recalculating and changing brightness again, adding flicker,
+ // we will wait for the next pmic cycle to re-evaluate this value
+ // make act on it, if needed.
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ if (mPmicMonitor.isStopped()) {
+ mPmicMonitor.start();
+ }
+ }
+ } else { // Current power consumed is under the quota.
+ isActive = true;
+ targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ }
+ }
+
+ // Cap to lowest allowed brightness on device.
+ if (mPowerThrottlingConfigData != null) {
targetBrightnessCap = Math.max(targetBrightnessCap,
- mPowerThrottlingConfigData.brightnessLowestCapAllowed);
+ mPowerThrottlingConfigData.brightnessLowestCapAllowed);
}
if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
mIsActive = isActive;
+ Slog.i(TAG, "Power clamper changing current brightness cap mBrightnessCap: "
+ + mBrightnessCap + " to target brightness cap:" + targetBrightnessCap
+ + " for current screen brightness: " + mCurrentBrightness);
mBrightnessCap = targetBrightnessCap;
+ Slog.i(TAG, "Power clamper changed state: thermalStatus:" + mCurrentThermalLevel
+ + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
+ + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
+ + " mCustomAnimationRateSec:" + mCustomAnimationRateSecDeviceConfig);
+ mCustomAnimationRateSec = mCustomAnimationRateSecDeviceConfig;
mChangeListener.onChanged();
+ } else {
+ mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
}
}
@@ -234,6 +313,11 @@
private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
mHandler.post(() -> {
+ if (mCurrentThermalLevel != thermalStatus) {
+ mCurrentThermalLevelChanged = true;
+ } else {
+ mCurrentThermalLevelChanged = false;
+ }
mCurrentThermalLevel = thermalStatus;
mCurrentAvgPowerConsumed = avgPowerConsumed;
recalculateBrightnessCap();
@@ -244,14 +328,107 @@
if (mPowerThrottlingConfigData == null) {
return;
}
- PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
- recalculatePowerQuotaChange(powerConsumed, thermalStatus);
- };
- mPmicMonitor =
- mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
+ if (mPowerThrottlingConfigData.pollingWindowMaxMillis
+ <= mPowerThrottlingConfigData.pollingWindowMinMillis) {
+ Slog.e(TAG, "Brightness power max polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMaxMillis
+ + " msec, should be greater than brightness min polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+ return;
+ }
+ if ((mPowerThrottlingConfigData.pollingWindowMaxMillis
+ % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) {
+ Slog.e(TAG, "Brightness power max polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMaxMillis
+ + " msec, is not divisible by brightness min polling window:"
+ + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+ return;
+ }
+ mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+ mThermalLevelListener.start();
+ }
+
+ private void activatePmicMonitor() {
+ if (!mPmicMonitor.isStopped()) {
+ return;
+ }
mPmicMonitor.start();
}
+ private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) {
+ if (status != Temperature.THROTTLING_NONE) {
+ return;
+ }
+ if (mPmicMonitor.isStopped()) {
+ return;
+ }
+ mPmicMonitor.stop();
+ }
+
+ private final class ThermalLevelListener extends IThermalEventListener.Stub {
+ private final Handler mHandler;
+ private IThermalService mThermalService;
+ private boolean mStarted;
+
+ ThermalLevelListener(Handler handler) {
+ mHandler = handler;
+ mStarted = false;
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ }
+
+ IThermalService getThermalService() {
+ return mThermalService;
+ }
+
+ void start() {
+ if (mStarted) {
+ return;
+ }
+ if (mThermalService == null) {
+ return;
+ }
+ try {
+ // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
+ mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ @Temperature.ThrottlingStatus int status = temp.getStatus();
+ if (status >= Temperature.THROTTLING_LIGHT) {
+ Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status);
+ mHandler.post(() -> activatePmicMonitor());
+ } else {
+ if (!mPmicMonitor.isStopped()) {
+ mHandler.post(() -> deactivatePmicMonitor(status));
+ }
+ }
+ }
+
+ void stop() {
+ if (!mStarted) {
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" ThermalLevelObserver:");
+ writer.println(" mStarted: " + mStarted);
+ }
+ }
+
public interface PowerData {
@NonNull
String getUniqueDisplayId();
@@ -279,8 +456,12 @@
@VisibleForTesting
static class Injector {
- PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
- return new PmicMonitor(listener, pollingTime);
+ PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener,
+ IThermalService thermalService,
+ int pollingMaxTimeMillis,
+ int pollingMinTimeMillis) {
+ return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis,
+ pollingMinTimeMillis);
}
DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
index 26784f23..355f4fe 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
@@ -18,15 +18,12 @@
import static com.android.server.display.brightness.clamper.BrightnessPowerClamper.PowerChangeListener;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.IThermalService;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.Temperature;
import android.power.PowerStatsInternal;
import android.util.IntArray;
@@ -51,25 +48,30 @@
// The executor to periodically monitor the display power.
private final ScheduledExecutorService mExecutor;
- @NonNull
- private final PowerChangeListener mPowerChangeListener;
- private final long mPowerMonitorPeriodConfigSecs;
+ private final long mPowerMonitorPeriodConfigMillis;
private final PowerStatsInternal mPowerStatsInternal;
@VisibleForTesting final IThermalService mThermalService;
+ @VisibleForTesting PowerChangeListener mPowerChangeListener;
private ScheduledFuture<?> mPmicMonitorFuture;
private float mLastEnergyConsumed = 0;
- private float mCurrentAvgPower = 0;
+ private float mCurrentTotalAvgPower = 0;
private Temperature mCurrentTemperature;
private long mCurrentTimestampMillis = 0;
+ private float[] mAvgPowerCircularList;
+ private int mPowerListStart = 0;
+ private int mPowerListEnd = 0;
- PmicMonitor(PowerChangeListener listener, int powerMonitorPeriodConfigSecs) {
+ PmicMonitor(PowerChangeListener listener,
+ IThermalService thermalService,
+ int pollingMaxTimeMillis,
+ int pollingMinTimeMillis) {
mPowerChangeListener = listener;
+ mThermalService = thermalService;
mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
+ mAvgPowerCircularList = new float[pollingMaxTimeMillis / pollingMinTimeMillis];
// start a periodic worker thread.
mExecutor = Executors.newSingleThreadScheduledExecutor();
- mPowerMonitorPeriodConfigSecs = (long) powerMonitorPeriodConfigSecs;
+ mPowerMonitorPeriodConfigMillis = pollingMinTimeMillis;
}
@Nullable
@@ -141,12 +143,28 @@
// capture thermal state.
Temperature displayTemperature = getDisplayTemperature();
- mCurrentAvgPower = currentPower;
+ boolean isBufferFull = false;
+ mAvgPowerCircularList[mPowerListEnd] = currentPower;
+ mCurrentTotalAvgPower += currentPower;
+ mPowerListEnd =
+ (mPowerListEnd + 1) % mAvgPowerCircularList.length;
+ if (mPowerListStart == mPowerListEnd) {
+ isBufferFull = true;
+ }
+
mCurrentTemperature = displayTemperature;
mLastEnergyConsumed = displayResults[0].energyUWs;
mCurrentTimestampMillis = displayResults[0].timestampMs;
- if (mCurrentTemperature != null) {
- mPowerChangeListener.onChanged(mCurrentAvgPower, mCurrentTemperature.getStatus());
+
+ if (mCurrentTemperature != null && isBufferFull) {
+ mPowerChangeListener.onChanged(mCurrentTotalAvgPower / mAvgPowerCircularList.length,
+ mCurrentTemperature.getStatus());
+ }
+
+ // average power long-term list is full, reset values for next cycle.
+ if (isBufferFull) {
+ mCurrentTotalAvgPower = mCurrentTotalAvgPower - mAvgPowerCircularList[mPowerListStart];
+ mPowerListStart = (mPowerListStart + 1) % mAvgPowerCircularList.length;
}
}
@@ -165,11 +183,11 @@
if (mPmicMonitorFuture == null) {
mPmicMonitorFuture = mExecutor.scheduleAtFixedRate(
this::capturePeriodicDisplayPower,
- mPowerMonitorPeriodConfigSecs,
- mPowerMonitorPeriodConfigSecs,
- TimeUnit.SECONDS);
+ mPowerMonitorPeriodConfigMillis,
+ mPowerMonitorPeriodConfigMillis,
+ TimeUnit.MILLISECONDS);
} else {
- Slog.e(TAG, "already scheduled, stop() called before start.");
+ Slog.e(TAG, "PMIC already scheduled, stop() called before start.");
}
}
@@ -184,6 +202,23 @@
}
/**
+ * Updates PMIC configuration.
+ */
+ public void updateConfiguration() {
+ if (mPmicMonitorFuture != null) {
+ mPmicMonitorFuture.cancel(true);
+ mPmicMonitorFuture = null;
+ }
+ }
+
+ /**
+ * Returns if PMIC monitor is stopped.
+ */
+ public boolean isStopped() {
+ return mPmicMonitorFuture == null;
+ }
+
+ /**
* Shutdown power IC service and worker thread.
*/
public void shutdown() {
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
index 1db9bbe..91c985830 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
@@ -100,6 +100,7 @@
writer.println("AutoBrightnessFallbackStrategy:");
writer.println(" mLeadDisplayId=" + mLeadDisplayId);
writer.println(" mIsDisplayEnabled=" + mIsDisplayEnabled);
+ writer.println("");
if (mScreenOffBrightnessSensorController != null) {
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
mScreenOffBrightnessSensorController.dump(ipw);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 35be0f3..69b67c8 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -420,6 +420,7 @@
*/
public void dump(PrintWriter pw) {
pw.println("DisplayManagerFlags:");
+ pw.println("--------------------");
pw.println(" " + mAdaptiveToneImprovements1);
pw.println(" " + mAdaptiveToneImprovements2);
pw.println(" " + mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState);
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d909004..18e0d6e 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -578,7 +578,8 @@
* @param pw The stream to dump information to.
*/
public void dump(PrintWriter pw) {
- pw.println("DisplayModeDirector");
+ pw.println("DisplayModeDirector:");
+ pw.println("--------------------");
synchronized (mLock) {
pw.println(" mSupportedModesByDisplay:");
for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index 21bb208..dba6874 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -114,9 +114,9 @@
*
* @param pw The PrintWriter used to dump the state.
*/
- public void dumpsys(PrintWriter pw) {
- pw.println();
+ public void dump(PrintWriter pw) {
pw.println("DisplayStateController:");
+ pw.println("-----------------------");
pw.println(" mPerformScreenOffTransition:" + mPerformScreenOffTransition);
pw.println(" mDozeStateOverride=" + mDozeStateOverride);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 7ea576d..49c45a7 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -427,7 +427,8 @@
* The writer used to dump the state.
*/
public void dump(PrintWriter writer) {
- writer.println("DisplayWhiteBalanceController");
+ writer.println("DisplayWhiteBalanceController:");
+ writer.println("------------------------------");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mEnabled=" + mEnabled);
writer.println(" mStrongModeEnabled=" + mStrongModeEnabled);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
index 0efb749..a5755ac 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -23,7 +23,6 @@
import android.os.Message;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
@@ -129,7 +128,8 @@
* The writer used to dump the state.
*/
public void dump(PrintWriter writer) {
- writer.println("DisplayWhiteBalanceSettings");
+ writer.println("DisplayWhiteBalanceSettings:");
+ writer.println("----------------------------");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mContext=" + mContext);
writer.println(" mHandler=" + mHandler);
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 58e3452..e1f26d6 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -116,11 +116,11 @@
boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason);
+ @SoftInputShowHideReason int reason, boolean async);
boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async);
@PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
void hideSoftInputFromServerForTest();
@@ -132,7 +132,8 @@
@Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
InputBindResult startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client,
@@ -290,17 +291,17 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
}
@EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -336,11 +337,13 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
mCallback.startInputOrWindowGainedFocusAsync(
startInputReason, client, windowToken, startInputFlags, softInputMode,
windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
- unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+ unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq,
+ useAsyncShowHideMethod);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2ad0d2a..7cfd2cc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3086,7 +3086,7 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput", mDumper);
@@ -3533,7 +3533,7 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput", mDumper);
synchronized (ImfLock.class) {
@@ -3675,7 +3675,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
// implemented by ZeroJankProxy
}
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index c940a9c..214aa1d 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -77,6 +77,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* A proxy that processes all {@link IInputMethodManager} calls asynchronously.
@@ -175,19 +176,45 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- offload(() -> mInner.showSoftInput(
- client, windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason));
- return true;
+ @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.showSoftInput(
+ client, windowToken, statsToken, flags, lastClickToolType, resultReceiver,
+ reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.showSoftInput(
+ client,
+ windowToken,
+ statsToken,
+ flags,
+ lastClickToolType,
+ resultReceiver,
+ reason,
+ async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- offload(() -> mInner.hideSoftInput(
- client, windowToken, statsToken, flags, resultReceiver, reason));
- return true;
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
@@ -207,7 +234,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
offload(() -> {
InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dbe778e..54e9189 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7600,16 +7600,14 @@
+ " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
}
- if (android.app.Flags.secureAllowlistToken()) {
- IBinder allowlistToken = notification.getAllowlistToken();
- if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
- throw new SecurityException(
- "Unexpected allowlist token received from " + callingUid);
- }
- // allowlistToken is populated by unparceling, so it can be null if the notification was
- // posted from inside system_server. Ensure it's the expected value.
- notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
+ IBinder allowlistToken = notification.getAllowlistToken();
+ if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
+ throw new SecurityException(
+ "Unexpected allowlist token received from " + callingUid);
}
+ // allowlistToken is populated by unparceling, so it can be null if the notification was
+ // posted from inside system_server. Ensure it's the expected value.
+ notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
checkRestrictedCategories(notification);
@@ -8774,12 +8772,10 @@
*/
private boolean enqueueNotification() {
synchronized (mNotificationLock) {
- if (android.app.Flags.secureAllowlistToken()) {
- // allowlistToken is populated by unparceling, so it will be absent if the
- // EnqueueNotificationRunnable is created directly by NMS (as we do for group
- // summaries) instead of via notify(). Fix that.
- r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
- }
+ // allowlistToken is populated by unparceling, so it will be absent if the
+ // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+ // summaries) instead of via notify(). Fix that.
+ r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
final long snoozeAt =
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 6ff0e04..333b3e2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -226,10 +226,8 @@
mDefaultConfig = Flags.modesUi()
? ZenModeConfig.getDefaultConfig()
: readDefaultConfig(mContext.getResources());
- updateDefaultConfigAutomaticRules();
- if (Flags.modesApi()) {
- updateDefaultAutomaticRulePolicies();
- }
+ updateDefaultConfig(mContext, mDefaultConfig);
+
mConfig = mDefaultConfig.copy();
synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
@@ -1073,7 +1071,7 @@
}
void updateZenRulesOnLocaleChange() {
- updateDefaultConfigAutomaticRules();
+ updateRuleStringsForCurrentLocale(mContext, mDefaultConfig);
synchronized (mConfigLock) {
if (mConfig == null) {
return;
@@ -2229,30 +2227,49 @@
}
}
- private void updateDefaultConfigAutomaticRules() {
- for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ /**
+ * Apply changes to the <em>default</em> {@link ZenModeConfig} so that the rules included by
+ * default (Events / Sleeping) support the latest Zen features and are ready for new users.
+ *
+ * <p>This includes: setting a fully populated ZenPolicy, setting correct type and
+ * allowManualInvocation=true, and ensuring default names and trigger descriptions correspond
+ * to the current locale.
+ */
+ private static void updateDefaultConfig(Context context, ZenModeConfig defaultConfig) {
+ if (Flags.modesApi()) {
+ updateDefaultAutomaticRulePolicies(defaultConfig);
+ }
+ if (Flags.modesApi() && Flags.modesUi()) {
+ SystemZenRules.maybeUpgradeRules(context, defaultConfig);
+ }
+ updateRuleStringsForCurrentLocale(context, defaultConfig);
+ }
+
+ private static void updateRuleStringsForCurrentLocale(Context context,
+ ZenModeConfig defaultConfig) {
+ for (ZenRule rule : defaultConfig.automaticRules.values()) {
if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
- rule.name = mContext.getResources()
+ rule.name = context.getResources()
.getString(R.string.zen_mode_default_events_name);
} else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
- rule.name = mContext.getResources()
+ rule.name = context.getResources()
.getString(R.string.zen_mode_default_every_night_name);
}
if (Flags.modesApi() && Flags.modesUi()) {
- SystemZenRules.updateTriggerDescription(mContext, rule);
+ SystemZenRules.updateTriggerDescription(context, rule);
}
}
}
// Updates the policies in the default automatic rules (provided via default XML config) to
// be fully filled in default values.
- private void updateDefaultAutomaticRulePolicies() {
+ private static void updateDefaultAutomaticRulePolicies(ZenModeConfig defaultConfig) {
if (!Flags.modesApi()) {
// Should be checked before calling, but just in case.
return;
}
- ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy();
- for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+ ZenPolicy defaultPolicy = defaultConfig.getZenPolicy();
+ for (ZenRule rule : defaultConfig.automaticRules.values()) {
if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
rule.zenPolicy = defaultPolicy.copy();
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4c9be21..1f672a0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5575,18 +5575,18 @@
}
if (!paths.getOverlayPaths().isEmpty()) {
pw.print(prefix);
- pw.println(" ");
+ pw.print(" ");
pw.print(libOverlayPaths.getKey());
pw.println(" overlay paths:");
for (String path : paths.getOverlayPaths()) {
pw.print(prefix);
- pw.print(" ");
+ pw.print(" ");
pw.println(path);
}
}
if (!paths.getResourceDirs().isEmpty()) {
pw.print(prefix);
- pw.println(" ");
+ pw.print(" ");
pw.print(libOverlayPaths.getKey());
pw.println(" legacy overlay paths:");
for (String path : paths.getResourceDirs()) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 25468fa..a3ff195 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -588,7 +588,7 @@
void handleOnDefaultLauncherChanged(int userId) {
if (DEBUG) {
- Slog.v(TAG, "Default launcher changed for user: " + userId);
+ Slog.v(TAG, "Default launcher changed for userId=" + userId);
}
// Default launcher is removed or changed, revoke all URI permissions.
@@ -712,7 +712,7 @@
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: userId=" + userId);
}
synchronized (mUnlockedUsers) {
mUnlockedUsers.put(userId, true);
@@ -739,7 +739,7 @@
/** lifecycle event */
void handleStopUser(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "handleStopUser: user=" + userId);
+ Slog.d(TAG, "handleStopUser: userId=" + userId);
}
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
synchronized (mServiceLock) {
@@ -755,7 +755,7 @@
@GuardedBy("mServiceLock")
private void unloadUserLocked(int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "unloadUserLocked: user=" + userId);
+ Slog.d(TAG, "unloadUserLocked: userId=" + userId);
}
// Cancel any ongoing background tasks.
getUserShortcutsLocked(userId).cancelAllInFlightTasks();
@@ -1221,7 +1221,7 @@
private void scheduleSaveInner(@UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Scheduling to save for " + userId);
+ Slog.d(TAG, "Scheduling to save for userId=" + userId);
}
synchronized (mServiceLock) {
if (!mDirtyUserIds.contains(userId)) {
@@ -1333,7 +1333,7 @@
// Requires mServiceLock held, but "Locked" prefix would look weird so we just say "L".
void throwIfUserLockedL(@UserIdInt int userId) {
if (!isUserUnlockedL(userId)) {
- throw new IllegalStateException("User " + userId + " is locked or not running");
+ throw new IllegalStateException("User (with userId=" + userId + ") is locked or not running");
}
}
@@ -1720,7 +1720,7 @@
// Otherwise, make sure the arguments are valid.
if (UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Invalid user-ID");
+ throw new SecurityException("Invalid userId");
}
}
@@ -1735,7 +1735,7 @@
// Otherwise, make sure the arguments are valid.
if (UserHandle.getUserId(callingUid) != userId) {
- throw new SecurityException("Invalid user-ID");
+ throw new SecurityException("Invalid userId");
}
if (injectGetPackageUid(packageName, userId) != callingUid) {
throw new SecurityException("Calling package name mismatch");
@@ -1755,7 +1755,7 @@
}
final int callingUid = injectBinderCallingUid();
if (UserHandle.getUserId(callingUid) != si.getUserId()) {
- throw new SecurityException("User-ID in shortcut doesn't match the caller");
+ throw new SecurityException("UserId in shortcut doesn't match the caller");
}
}
@@ -1822,7 +1822,7 @@
final int userId = sp.getPackageUserId();
if (DEBUG) {
Slog.d(TAG, String.format(
- "Shortcut changes: package=%s, user=%d", packageName, userId));
+ "Shortcut changes: package=%s, userId=%d", packageName, userId));
}
injectPostToHandlerDebounced(sp, notifyListenerRunnable(packageName, userId));
notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
@@ -1832,7 +1832,7 @@
private void notifyListeners(@NonNull final String packageName, @UserIdInt final int userId) {
if (DEBUG) {
Slog.d(TAG, String.format(
- "Shortcut changes: package=%s, user=%d", packageName, userId));
+ "Shortcut changes: package=%s, userId=%d", packageName, userId));
}
injectPostToHandler(notifyListenerRunnable(packageName, userId));
}
@@ -2736,14 +2736,14 @@
void resetThrottlingInner(@UserIdInt int userId) {
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- Log.w(TAG, "User " + userId + " is locked or not running");
+ Log.w(TAG, "User (with userId=" + userId + ") is locked or not running");
return;
}
getUserShortcutsLocked(userId).resetThrottling();
}
scheduleSaveUser(userId);
- Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
+ Slog.i(TAG, "ShortcutManager: throttling counter reset for userId=" + userId);
}
void resetAllThrottlingInner() {
@@ -2755,7 +2755,7 @@
@Override
public void onApplicationActive(String packageName, int userId) {
if (DEBUG) {
- Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
+ Slog.d(TAG, "onApplicationActive: package=" + packageName + " userId=" + userId);
}
enforceResetThrottlingPermission();
synchronized (mServiceLock) {
@@ -2822,7 +2822,7 @@
if (defaultLauncher != null) {
if (DEBUG) {
- Slog.v(TAG, "Detected launcher: " + defaultLauncher + " user: " + userId);
+ Slog.v(TAG, "Detected launcher: " + defaultLauncher + " userId=" + userId);
}
return defaultLauncher.equals(packageName);
} else {
@@ -2875,11 +2875,11 @@
if (defaultLauncher != null) {
if (DEBUG) {
Slog.v(TAG, "Default launcher from RoleManager: " + defaultLauncher
- + " user: " + userId);
+ + " userId=" + userId);
}
user.setCachedLauncher(defaultLauncher);
} else {
- Slog.e(TAG, "Default launcher not found." + " user: " + userId);
+ Slog.e(TAG, "Default launcher not found." + " userId=" + userId);
}
return defaultLauncher;
@@ -2974,7 +2974,7 @@
int queryFlags, int userId, int callingPid, int callingUid) {
if (DEBUG_REBOOT) {
Slog.d(TAG, "Getting shortcuts for launcher= " + callingPackage
- + "user=" + userId + " pkg=" + packageName);
+ + "userId=" + userId + " pkg=" + packageName);
}
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
@@ -3793,7 +3793,7 @@
if (!isUserUnlockedL(userId)) {
if (DEBUG) {
Slog.d(TAG, "Ignoring package broadcast " + action
- + " for locked/stopped user " + userId);
+ + " for locked/stopped userId=" + userId);
}
return;
}
@@ -3814,10 +3814,10 @@
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
if (replacing) {
- Slog.d(TAG, "replacing package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "replacing package: " + packageName + " userId=" + userId);
handlePackageUpdateFinished(packageName, userId);
} else {
- Slog.d(TAG, "adding package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "adding package: " + packageName + " userId=" + userId);
handlePackageAdded(packageName, userId);
}
break;
@@ -3825,21 +3825,21 @@
if (!replacing || (replacing && archival)) {
if (!replacing) {
Slog.d(TAG, "removing package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
} else if (archival) {
Slog.d(TAG, "archiving package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
}
handlePackageRemoved(packageName, userId);
}
break;
case Intent.ACTION_PACKAGE_CHANGED:
- Slog.d(TAG, "changing package: " + packageName + " userId" + userId);
+ Slog.d(TAG, "changing package: " + packageName + " userId=" + userId);
handlePackageChanged(packageName, userId);
break;
case Intent.ACTION_PACKAGE_DATA_CLEARED:
Slog.d(TAG, "clearing data for package: "
- + packageName + " userId" + userId);
+ + packageName + " userId=" + userId);
handlePackageDataCleared(packageName, userId);
break;
}
@@ -3902,7 +3902,7 @@
if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
if (DEBUG) {
Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
- + " user " + spi.getPackageUserId());
+ + " userId=" + spi.getPackageUserId());
}
gonePackages.add(
UserPackage.of(spi.getPackageUserId(), spi.getPackageName()));
@@ -3927,7 +3927,7 @@
@GuardedBy("mServiceLock")
private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
if (DEBUG_REBOOT) {
- Slog.d(TAG, "rescan updated package user=" + userId + " last scanned=" + lastScanTime);
+ Slog.d(TAG, "rescan updated package userId=" + userId + " last scanned=" + lastScanTime);
}
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3953,7 +3953,7 @@
private void handlePackageAdded(String packageName, @UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
+ Slog.d(TAG, String.format("handlePackageAdded: %s userId=%d", packageName, userId));
}
synchronized (mServiceLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3965,7 +3965,7 @@
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
+ Slog.d(TAG, String.format("handlePackageUpdateFinished: %s userId=%d",
packageName, userId));
}
synchronized (mServiceLock) {
@@ -3981,7 +3981,7 @@
private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageRemoved: %s userId=%d", packageName,
packageUserId));
}
cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
@@ -3991,7 +3991,7 @@
private void handlePackageDataCleared(String packageName, int packageUserId) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageDataCleared: %s userId=%d", packageName,
packageUserId));
}
cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
@@ -4006,7 +4006,7 @@
return;
}
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
+ Slog.d(TAG, String.format("handlePackageChanged: %s userId=%d", packageName,
packageUserId));
}
@@ -4193,7 +4193,7 @@
private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
Consumer<ApplicationInfo> callback) {
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
+ Slog.d(TAG, "forUpdatedPackages for userId=" + userId + ", lastScanTime=" + lastScanTime
+ " afterOta=" + afterOta);
}
final List<PackageInfo> list = getInstalledPackages(userId);
@@ -4302,7 +4302,7 @@
return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
.getPackageManager().getResourcesForApplication(packageName);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+ Slog.e(TAG, "Resources of package " + packageName + " for userId=" + userId
+ " not found");
return null;
} finally {
@@ -4512,17 +4512,17 @@
public byte[] getBackupPayload(@UserIdInt int userId) {
enforceSystem();
if (DEBUG) {
- Slog.d(TAG, "Backing up user " + userId);
+ Slog.d(TAG, "Backing up user with userId=" + userId);
}
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- wtf("Can't backup: user " + userId + " is locked or not running");
+ wtf("Can't backup: userId=" + userId + " is locked or not running");
return null;
}
final ShortcutUser user = getUserShortcutsLocked(userId);
if (user == null) {
- wtf("Can't backup: user not found: id=" + userId);
+ wtf("Can't backup: user not found: userId=" + userId);
return null;
}
@@ -4562,11 +4562,11 @@
public void applyRestore(byte[] payload, @UserIdInt int userId) {
enforceSystem();
if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Restoring user " + userId);
+ Slog.d(TAG, "Restoring user with userId=" + userId);
}
synchronized (mServiceLock) {
if (!isUserUnlockedL(userId)) {
- wtf("Can't restore: user " + userId + " is locked or not running");
+ wtf("Can't restore: user (with userId=" + userId + ") is locked or not running");
return;
}
// Note we print the file timestamps in dumpsys too, but also printing the timestamp
@@ -4989,7 +4989,7 @@
mUserId = UserHandle.parseUserArg(getNextArgRequired());
if (!isUserUnlockedL(mUserId)) {
throw new CommandException(
- "User " + mUserId + " is not running or locked");
+ "User (with userId=" + mUserId + ") is not running or locked");
}
break;
}
@@ -5094,7 +5094,7 @@
synchronized (mServiceLock) {
parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleResetThrottling: userId=" + mUserId);
resetThrottlingInner(mUserId);
}
@@ -5136,7 +5136,7 @@
final String defaultLauncher = getDefaultLauncher(mUserId);
if (defaultLauncher == null) {
throw new CommandException(
- "Failed to get the default launcher for user " + mUserId);
+ "Failed to get the default launcher for userId=" + mUserId);
}
// Get the class name of the component from PM to keep the old behaviour.
@@ -5157,7 +5157,7 @@
synchronized (mServiceLock) {
parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleUnloadUser: userId=" + mUserId);
ShortcutService.this.handleStopUser(mUserId);
}
@@ -5168,7 +5168,7 @@
parseOptionsLocked(/* takeUser =*/ true);
final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
+ Slog.i(TAG, "cmd: handleClearShortcuts: userId=" + mUserId + ", " + packageName);
ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
/* appStillExists = */ true);
@@ -5180,7 +5180,7 @@
parseOptionsLocked(/* takeUser =*/ true);
final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+ Slog.i(TAG, "cmd: handleGetShortcuts: userId=" + mUserId + ", flags="
+ mShortcutMatchFlags + ", package=" + packageName);
final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index 968ff59..eda222e 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import android.os.Process;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -122,6 +123,9 @@
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ @VisibleForTesting
+ static final String SYSTEM_PACKAGE_NAME = "System";
+
/**
* Lock protects WakeLockLog.dump (binder thread) from conflicting with changes to the log
* happening on the background thread.
@@ -516,21 +520,26 @@
return;
}
- String[] packages;
- if (uidToPackagesCache.contains(tag.ownerUid)) {
- packages = uidToPackagesCache.get(tag.ownerUid);
- } else {
- packages = packageManager.getPackagesForUid(tag.ownerUid);
- uidToPackagesCache.put(tag.ownerUid, packages);
+ if (tag.ownerUid == Process.SYSTEM_UID) {
+ packageName = SYSTEM_PACKAGE_NAME;
}
+ else {
+ String[] packages;
+ if (uidToPackagesCache.contains(tag.ownerUid)) {
+ packages = uidToPackagesCache.get(tag.ownerUid);
+ } else {
+ packages = packageManager.getPackagesForUid(tag.ownerUid);
+ uidToPackagesCache.put(tag.ownerUid, packages);
+ }
- if (packages != null && packages.length > 0) {
- packageName = packages[0];
- if (packages.length > 1) {
- StringBuilder sb = new StringBuilder();
- sb.append(packageName)
- .append(",...");
- packageName = sb.toString();
+ if (packages != null && packages.length > 0) {
+ packageName = packages[0];
+ if (packages.length > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(packageName)
+ .append(",...");
+ packageName = sb.toString();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 5e35925..6740153 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -326,5 +326,5 @@
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
+ | PackageManager.MATCH_ANY_USER;
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 2ce1aa42..fb5c115 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -838,12 +838,13 @@
}
if (android.app.Flags.appStartInfoTimestamps()) {
+ final int pid = r.getPid();
// Log here to match StatsD for time to first frame.
mLoggerHandler.post(
() -> mSupervisor.mService.mWindowManager.mAmInternal.addStartInfoTimestamp(
ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME,
- timestampNs, r.getUid(), r.getPid(),
- info.mLastLaunchedActivity.mUserId));
+ timestampNs, infoSnapshot.applicationInfo.uid, pid,
+ infoSnapshot.userId));
}
return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ebdf52c..e562ea8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8152,6 +8152,9 @@
*/
@Override
protected int getOverrideOrientation() {
+ if (mWmService.mConstants.mIgnoreActivityOrientationRequest) {
+ return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
return mAppCompatController.getOrientationPolicy()
.overrideOrientationIfNeeded(super.getOverrideOrientation());
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index a5cea34..2259b5a 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -21,10 +21,10 @@
import static android.app.ActivityOptions.BackgroundActivityStartMode;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -39,13 +39,15 @@
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;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
+import static com.android.window.flags.Flags.balAdditionalStartModes;
import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck;
+import static com.android.window.flags.Flags.balImprovedMetrics;
import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
import static com.android.window.flags.Flags.balRequireOptInSameUid;
import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
@@ -84,6 +86,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -107,6 +110,17 @@
private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
private static final int NO_PROCESS_UID = -1;
+ private static final BalCheckConfiguration BAL_CHECK_FOREGROUND = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ false,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+ private static final BalCheckConfiguration BAL_CHECK_BACKGROUND = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ false,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
static final String AUTO_OPT_IN_SAME_UID = "sameUid";
@@ -412,6 +426,8 @@
int callingUid, String callingPackage, ActivityOptions checkedOptions) {
switch (checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()) {
case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE:
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS:
return BackgroundStartPrivileges.ALLOW_BAL;
case MODE_BACKGROUND_ACTIVITY_START_DENIED:
return BackgroundStartPrivileges.NONE;
@@ -752,7 +768,7 @@
// PendingIntents is null).
BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
? resultForCaller
- : checkBackgroundActivityStartAllowedBySender(state)
+ : checkBackgroundActivityStartAllowedByRealCaller(state)
.setBasedOnRealCaller();
state.setResultForRealCaller(resultForRealCaller);
@@ -827,6 +843,37 @@
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
+ if (state.isPendingIntent()) {
+ // PendingIntents should mostly be allowed by the sender (real caller) or a permission
+ // the creator of the PendingIntent has. Visibility should be the exceptional case, so
+ // test it last (this does not change the result, just the bal code).
+ BalVerdict result = BalVerdict.BLOCK;
+ if (!(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+ }
+ if (result == BalVerdict.BLOCK) {
+ result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+
+ }
+ return result;
+ } else {
+ BalVerdict result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+ if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByCallerInForeground(BalState state) {
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
@@ -842,7 +889,16 @@
return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
/*background*/ false, "callingUid has non-app visible window");
}
+ // Don't abort if the callerApp or other processes of that uid are considered to be in the
+ // foreground.
+ return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_FOREGROUND);
+ }
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByCallerInBackground(BalState state) {
// don't abort for the most important UIDs
final int callingAppId = UserHandle.getAppId(state.mCallingUid);
if (state.mCallingUid == Process.ROOT_UID
@@ -922,25 +978,29 @@
"OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
}
- // If we don't have callerApp at this point, no caller was provided to startActivity().
- // That's the case for PendingIntent-based starts, since the creator's process might not be
- // up and alive.
// Don't abort if the callerApp or other processes of that uid are allowed in any way.
- BalVerdict callerAppAllowsBal = checkProcessAllowsBal(state.mCallerApp, state);
- if (callerAppAllowsBal.allows()) {
- return callerAppAllowsBal;
- }
-
- // If we are here, it means all exemptions based on the creator failed
- return BalVerdict.BLOCK;
+ return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_BACKGROUND);
}
/**
* @return A code denoting which BAL rule allows an activity to be started,
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
- BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+ BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
+ BalVerdict result = checkBackgroundActivityStartAllowedByRealCallerInForeground(state);
+ if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+ && state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+ result = checkBackgroundActivityStartAllowedByRealCallerInBackground(state);
+ }
+ return result;
+ }
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByRealCallerInForeground(BalState state) {
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
// The home app can start apps even if app switches are usually disallowed.
@@ -966,6 +1026,16 @@
}
}
+ // Don't abort if the realCallerApp or other processes of that uid are considered to be in
+ // the foreground.
+ return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_FOREGROUND);
+ }
+
+ /**
+ * @return A code denoting which BAL rule allows an activity to be started,
+ * or {@link #BAL_BLOCK} if the launch should be blocked
+ */
+ BalVerdict checkBackgroundActivityStartAllowedByRealCallerInBackground(BalState state) {
if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
== MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
&& hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
@@ -992,14 +1062,7 @@
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
- BalVerdict realCallerAppAllowsBal =
- checkProcessAllowsBal(state.mRealCallerApp, state);
- if (realCallerAppAllowsBal.allows()) {
- return realCallerAppAllowsBal;
- }
-
- // If we are here, it means all exemptions based on PI sender failed
- return BalVerdict.BLOCK;
+ return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_BACKGROUND);
}
@VisibleForTesting boolean hasBalPermission(int uid, int pid) {
@@ -1015,13 +1078,13 @@
* exceptions.
*/
@VisibleForTesting BalVerdict checkProcessAllowsBal(WindowProcessController app,
- BalState state) {
+ BalState state, BalCheckConfiguration balCheckConfiguration) {
if (app == null) {
return BalVerdict.BLOCK;
}
// first check the original calling process
final BalVerdict balAllowedForCaller = app
- .areBackgroundActivityStartsAllowed(state.mAppSwitchState);
+ .areBackgroundActivityStartsAllowed(state.mAppSwitchState, balCheckConfiguration);
if (balAllowedForCaller.allows()) {
return balAllowedForCaller.withProcessInfo("callerApp process", app);
} else {
@@ -1033,7 +1096,7 @@
final WindowProcessController proc = uidProcesses.valueAt(i);
if (proc != app) {
BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
- state.mAppSwitchState);
+ state.mAppSwitchState, balCheckConfiguration);
if (balAllowedForUid.allows()) {
return balAllowedForUid.withProcessInfo("process", proc);
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 5f5365d..1073713 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -98,42 +98,63 @@
mBackgroundActivityStartCallback = callback;
}
+ record BalCheckConfiguration(
+ boolean isCheckingForFgsStart,
+ boolean checkVisibility,
+ boolean checkOtherExemptions,
+ long gracePeriod
+ ) {
+ }
+
+ /**
+ * Check configuration for foreground service starts.
+ *
+ * The check executes all parts of the BAL checks and uses the same grace period,
+ * so FGS is allowed whenever BAL is allowed.
+ */
+ static final BalCheckConfiguration CHECK_FOR_FGS_START = new BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ true,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
BalVerdict areBackgroundActivityStartsAllowed(
int pid, int uid, String packageName,
- int appSwitchState, boolean isCheckingForFgsStart,
+ int appSwitchState, BalCheckConfiguration checkConfiguration,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// Allow if the proc is instrumenting with background activity starts privs.
- if (hasBackgroundActivityStartPrivileges) {
+ if (checkConfiguration.checkOtherExemptions && hasBackgroundActivityStartPrivileges) {
return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
"process instrumenting with background activity starts privileges");
}
// Allow if the flag was explicitly set.
- if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
+ if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid,
+ packageName, checkConfiguration.isCheckingForFgsStart)) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION,
/*background*/ true, "process allowed by token");
}
// Allow if the caller is bound by a UID that's currently foreground.
// But still respect the appSwitchState.
- boolean allowBoundByForegroundUid =
+ if (checkConfiguration.checkVisibility && (
Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid()
- ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
- : isBoundByForegroundUid();
- if (allowBoundByForegroundUid) {
+ ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
+ : isBoundByForegroundUid())) {
return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
: BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
"process bound by foreground uid");
}
// Allow if the caller has an activity in any foreground task.
- if (hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW) {
+ if (checkConfiguration.checkVisibility && hasActivityInVisibleTask
+ && appSwitchState != APP_SWITCH_DISALLOW) {
return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
"process has activity in foreground task");
}
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
- if (appSwitchState == APP_SWITCH_ALLOW) {
+ if (checkConfiguration.checkOtherExemptions && 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.
if (lastActivityLaunchTime > lastStopAppSwitchesTime
@@ -141,9 +162,9 @@
final long now = SystemClock.uptimeMillis();
long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime,
lastActivityFinishTime);
- if (timeSinceLastStartOrFinish < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
+ if (timeSinceLastStartOrFinish < checkConfiguration.gracePeriod) {
return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
- "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period ("
+ "within " + checkConfiguration.gracePeriod + "ms grace period ("
+ timeSinceLastStartOrFinish + "ms)");
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 1931be4..47c42f4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -34,6 +34,10 @@
*/
final class WindowManagerConstants {
+ /** The orientation of activity will be always "unspecified". */
+ private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
+ "ignore_activity_orientation_request";
+
/**
* The minimum duration between gesture exclusion logging for a given window in
* milliseconds.
@@ -58,6 +62,9 @@
/** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
boolean mSystemGestureExcludedByPreQStickyImmersive;
+ /** @see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST */
+ boolean mIgnoreActivityOrientationRequest;
+
private final WindowManagerGlobalLock mGlobalLock;
private final Runnable mUpdateSystemGestureExclusionCallback;
private final DeviceConfigInterface mDeviceConfig;
@@ -89,6 +96,7 @@
updateSystemGestureExclusionLogDebounceMillis();
updateSystemGestureExclusionLimitDp();
updateSystemGestureExcludedByPreQStickyImmersive();
+ updateIgnoreActivityOrientationRequest();
}
private void onAndroidPropertiesChanged(DeviceConfig.Properties properties) {
@@ -127,6 +135,9 @@
case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
updateSystemGestureExclusionLogDebounceMillis();
break;
+ case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST:
+ updateIgnoreActivityOrientationRequest();
+ break;
default:
break;
}
@@ -152,6 +163,12 @@
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
}
+ private void updateIgnoreActivityOrientationRequest() {
+ mIgnoreActivityOrientationRequest = mDeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
+ }
+
void dump(PrintWriter pw) {
pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
@@ -161,6 +178,8 @@
pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+ pw.print(" "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
+ pw.print("="); pw.println(mIgnoreActivityOrientationRequest);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index d96ebc6..b6b36c7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -89,6 +89,7 @@
import com.android.server.Watchdog;
import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
import com.android.server.wm.ActivityTaskManagerService.HotPath;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import java.io.IOException;
import java.io.PrintWriter;
@@ -695,20 +696,13 @@
public boolean areBackgroundFgsStartsAllowed() {
return areBackgroundActivityStartsAllowed(
mAtm.getBalAppSwitchesState(),
- true /* isCheckingForFgsStart */).allows();
+ BackgroundLaunchProcessController.CHECK_FOR_FGS_START).allows();
}
BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
- int appSwitchState) {
- return areBackgroundActivityStartsAllowed(
- appSwitchState,
- false /* isCheckingForFgsStart */);
- }
-
- private BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
- int appSwitchState, boolean isCheckingForFgsStart) {
+ int appSwitchState, BalCheckConfiguration checkConfiguration) {
return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid,
- mInfo.packageName, appSwitchState, isCheckingForFgsStart,
+ mInfo.packageName, appSwitchState, checkConfiguration,
hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 4231149..0eafb59 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -42,7 +42,7 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <xs:element type="thermalThrottling" name="thermalThrottling">
+ <xs:element type="thermalThrottling" name="thermalThrottling" minOccurs="0" maxOccurs="1">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
@@ -464,7 +464,15 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger">
+ <xs:element name="customAnimationRateSec" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="pollingWindowMaxMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="pollingWindowMinMillis" type="xs:nonNegativeInteger">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index cec2787..355b0ab 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -345,10 +345,14 @@
public class PowerThrottlingConfig {
ctor public PowerThrottlingConfig();
method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
- method @NonNull public final java.math.BigInteger getPollingWindowMillis();
+ method @NonNull public final java.math.BigDecimal getCustomAnimationRateSec();
+ method @NonNull public final java.math.BigInteger getPollingWindowMaxMillis();
+ method @NonNull public final java.math.BigInteger getPollingWindowMinMillis();
method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap();
method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal);
- method public final void setPollingWindowMillis(@NonNull java.math.BigInteger);
+ method public final void setCustomAnimationRateSec(@NonNull java.math.BigDecimal);
+ method public final void setPollingWindowMaxMillis(@NonNull java.math.BigInteger);
+ method public final void setPollingWindowMinMillis(@NonNull java.math.BigInteger);
}
public class PowerThrottlingMap {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 5eec012..b982098 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1325,11 +1325,6 @@
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 a08af72..4beb6a8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -230,11 +230,9 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
- policyDefinition, userId)) {
- return;
- }
+ if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+ policyDefinition, userId)) {
+ return;
}
if (policyDefinition.isNonCoexistablePolicy()) {
@@ -354,9 +352,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
- }
+ decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -500,11 +496,9 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
- policyDefinition, UserHandle.USER_ALL)) {
- return;
- }
+ 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
@@ -571,9 +565,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- decreasePolicySizeForAdmin(policyState, enforcingAdmin);
- }
+ decreasePolicySizeForAdmin(policyState, enforcingAdmin);
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
@@ -1739,25 +1731,23 @@
pw.println();
}
pw.decreaseIndent();
- if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- pw.println();
+ 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.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();
}
}
@@ -2018,23 +2008,21 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- 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);
- }
+ 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);
}
}
}
@@ -2042,9 +2030,6 @@
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);
@@ -2192,9 +2177,6 @@
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 886ae7a..fc61967 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1328,9 +1328,7 @@
Bundle prevRestrictions) {
resetCrossProfileIntentFiltersIfNeeded(userId, newRestrictions, prevRestrictions);
resetUserVpnIfNeeded(userId, newRestrictions, prevRestrictions);
- if (Flags.deletePrivateSpaceUnderRestriction()) {
- removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
- }
+ removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
}
private void resetUserVpnIfNeeded(
@@ -3695,9 +3693,6 @@
}
revertTransferOwnershipIfNecessaryLocked();
- if (!Flags.policyEngineMigrationV2Enabled()) {
- updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
- }
}
// Check whether work apps were paused via suspension and unsuspend if necessary.
@@ -9377,8 +9372,7 @@
void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) {
if (userId == UserHandle.USER_ALL) {
- if (Flags.headlessDeviceOwnerDelegateSecurityLoggingBugFix()
- && getHeadlessDeviceOwnerModeForDeviceOwner()
+ if (getHeadlessDeviceOwnerModeForDeviceOwner()
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
userId = mOwners.getDeviceOwnerUserId();
} else {
@@ -12452,12 +12446,6 @@
}
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) {
@@ -14320,10 +14308,6 @@
return;
}
- if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
- PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
- }
-
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
synchronized (getLockObject()) {
int affectedUser = getAffectedUser(parent);
@@ -14934,11 +14918,6 @@
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);
@@ -15219,7 +15198,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(
isProfileOwner(caller) || isDefaultDeviceOwner(caller));
- if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+ if (parent) {
Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
}
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
@@ -15230,7 +15209,7 @@
"Permission denial: device owners cannot update %1$s", setting));
}
int affectedUser;
- if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+ if (parent) {
affectedUser = getProfileParentId(caller.getUserId());
} else {
affectedUser = caller.getUserId();
@@ -16822,13 +16801,11 @@
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- 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);
- }
+ final UserHandle user = UserHandle.of(userId);
+ final String roleHolderPackage = getRoleHolderPackageNameOnUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
+ if (roleHolderPackage != null) {
+ broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
}
}
});
@@ -16836,18 +16813,10 @@
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
- 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));
- }
+ CallerIdentity caller = getCallerIdentity(admin, callerPackage);
+ enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
+ caller.getUserId());
return mOwners.getSystemUpdateInfo();
}
@@ -17391,17 +17360,10 @@
@Nullable ComponentName componentName, @UserIdInt int callingUserId) {
synchronized (getLockObject()) {
int deviceOwnerUserId = -1;
- 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;
- }
+ deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+ && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
+ == 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.
@@ -21442,13 +21404,7 @@
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
- enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
- } else {
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
- }
+ enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
@@ -22047,16 +22003,9 @@
final long identity = Binder.clearCallingIdentity();
try {
boolean isSingleUserMode;
- 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;
- }
+ int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
+ deviceAdmin, caller.getUserId());
+ isSingleUserMode = headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
if (Flags.headlessSingleMinTargetSdk()
&& mInjector.userManagerIsHeadlessSystemUserMode()
@@ -22455,35 +22404,17 @@
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- 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.");
+ synchronized (getLockObject()) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+ caller.getPackageName(),
+ caller.getUserId());
Preconditions.checkState(canUsbDataSignalingBeDisabled(),
"USB data signaling cannot be disabled.");
- }
-
- 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());
- }
- }
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ enforcingAdmin,
+ new BooleanPolicyValue(enabled));
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22505,24 +22436,10 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- 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();
- }
- }
- }
+ Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.USB_DATA_SIGNALING,
+ caller.getUserId());
+ return enabled == null || enabled;
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -24875,9 +24792,6 @@
@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());
@@ -24891,9 +24805,6 @@
@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());
@@ -24903,9 +24814,6 @@
@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());
@@ -24916,9 +24824,6 @@
@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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 13c436d..ab459df 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2467,8 +2467,8 @@
reportWtf("starting RuntimeService", e);
}
t.traceEnd();
-
- if (!isWatch && !disableNetworkTime) {
+ if (!disableNetworkTime && (!isWatch || (isWatch
+ && android.server.Flags.allowNetworkTimeUpdateService()))) {
t.traceBegin("StartNetworkTimeUpdateService");
try {
networkTimeUpdater = new NetworkTimeUpdateService(context);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 29f3871..ec74ef19 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -28,4 +28,11 @@
namespace: "wear_frameworks"
description: "Allow removing VpnManagerService"
bug: "340928692"
+}
+
+flag {
+ name: "allow_network_time_update_service"
+ namespace: "wear_systems"
+ description: "Allow NetworkTimeUpdateService on Wear"
+ bug: "327508176"
}
\ No newline at end of file
diff --git a/services/tests/appfunctions/OWNERS b/services/tests/appfunctions/OWNERS
new file mode 100644
index 0000000..7fa8917
--- /dev/null
+++ b/services/tests/appfunctions/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1627156
+include platform/frameworks/base:/core/java/android/app/appfunctions/OWNERS
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index d450683..fd05b26 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -263,7 +263,9 @@
mDisplayDeviceConfig.getPowerThrottlingConfigData();
assertNotNull(powerThrottlingConfigData);
assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
- assertEquals(10, powerThrottlingConfigData.pollingWindowMillis);
+ assertEquals(15f, powerThrottlingConfigData.customAnimationRateSec, SMALL_DELTA);
+ assertEquals(20000, powerThrottlingConfigData.pollingWindowMaxMillis);
+ assertEquals(10000, powerThrottlingConfigData.pollingWindowMinMillis);
}
@Test
@@ -1295,7 +1297,9 @@
private String getPowerThrottlingConfig() {
return "<powerThrottlingConfig >\n"
+ "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
- + "<pollingWindowMillis>10</pollingWindowMillis>\n"
+ + "<customAnimationRateSec>15</customAnimationRateSec>\n"
+ + "<pollingWindowMaxMillis>20000</pollingWindowMaxMillis>\n"
+ + "<pollingWindowMinMillis>10000</pollingWindowMinMillis>\n"
+ "<powerThrottlingMap>\n"
+ "<powerThrottlingPoint>\n"
+ "<thermalStatus>light</thermalStatus>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 2166cb7..d0aec3b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2587,7 +2587,7 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags, SensorManager sensorManager) {
+ DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
return mClamperController;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 0a03702..f9dc122 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -358,7 +358,7 @@
private BrightnessClamperController createBrightnessClamperController() {
return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
- mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager);
+ mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager, 0);
}
interface TestDisplayListenerModifier extends BrightnessStateModifier,
@@ -396,7 +396,7 @@
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data,
- DisplayManagerFlags flags, Context context) {
+ DisplayManagerFlags flags, Context context, float currentBrightness) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
index b3f33ad..c4898da 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Temperature;
@@ -58,12 +59,18 @@
private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
new FakeDeviceConfigInterface();
private final TestHandler mTestHandler = new TestHandler(null);
+ private final TestInjector mTestInjector = new TestInjector();
private BrightnessPowerClamper mClamper;
+ private final float mCurrentBrightness = 0.6f;
+ private PowerChangeListener mPowerChangeListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mClamper = new BrightnessPowerClamper(new TestInjector(), mTestHandler,
- mMockClamperChangeListener, new TestPowerData());
+ mClamper = new BrightnessPowerClamper(mTestInjector, mTestHandler,
+ mMockClamperChangeListener, new TestPowerData(), mCurrentBrightness);
+ mPowerChangeListener = mClamper.getPowerChangeListener();
+ mPmicMonitor = mTestInjector.getPmicMonitor(mPowerChangeListener, null, 5, 10);
+ mPmicMonitor.setPowerChangeListener(mPowerChangeListener);
mTestHandler.flush();
}
@@ -79,36 +86,27 @@
}
@Test
- public void testPowerThrottlingNoOngoingAnimation() throws RemoteException {
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
+ public void testPowerThrottlingWithThermalLevelLight() throws RemoteException {
+ mPmicMonitor.setThermalStatus(Temperature.THROTTLING_LIGHT);
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
// update a new device config for power-throttling.
mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 100f))));
+ List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_LIGHT, 100f))));
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
// Assume current brightness as max, as there is no throttling.
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
- // update a new device config for power-throttling.
- mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
- mPmicMonitor.setAvgPowerConsumed(100f);
- expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
- mTestHandler.flush();
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
@Test
- public void testPowerThrottlingWithOngoingAnimation() throws RemoteException {
+ public void testPowerThrottlingWithThermalLevelSevere() throws RemoteException {
mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
mTestHandler.flush();
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -119,20 +117,10 @@
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
// Assume current brightness as max, as there is no throttling.
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
- // update a new device config for power-throttling.
- mClamper.onDisplayChanged(new TestPowerData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
- mPmicMonitor.setAvgPowerConsumed(100f);
- expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
- mTestHandler.flush();
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
@Test
@@ -148,8 +136,7 @@
mPmicMonitor.setAvgPowerConsumed(200f);
float expectedBrightness = 0.5f;
- expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+ expectedBrightness = expectedBrightness * mCurrentBrightness;
mTestHandler.flush();
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -169,10 +156,11 @@
private static class TestPmicMonitor extends PmicMonitor {
private Temperature mCurrentTemperature;
- private final PowerChangeListener mListener;
- TestPmicMonitor(PowerChangeListener listener, int pollingTime) {
- super(listener, pollingTime);
- mListener = listener;
+ private PowerChangeListener mListener;
+ TestPmicMonitor(PowerChangeListener listener,
+ IThermalService thermalService,
+ int pollingTimeMax, int pollingTimeMin) {
+ super(listener, thermalService, pollingTimeMax, pollingTimeMin);
}
public void setAvgPowerConsumed(float power) {
int status = mCurrentTemperature.getStatus();
@@ -181,13 +169,18 @@
public void setThermalStatus(@Temperature.ThrottlingStatus int status) {
mCurrentTemperature = new Temperature(100, Temperature.TYPE_SKIN, "test_temp", status);
}
+ public void setPowerChangeListener(PowerChangeListener listener) {
+ mListener = listener;
+ }
}
private class TestInjector extends BrightnessPowerClamper.Injector {
@Override
TestPmicMonitor getPmicMonitor(PowerChangeListener listener,
- int pollingTime) {
- mPmicMonitor = new TestPmicMonitor(listener, pollingTime);
+ IThermalService thermalService,
+ int minPollingTimeMillis, int maxPollingTimeMillis) {
+ mPmicMonitor = new TestPmicMonitor(listener, thermalService, maxPollingTimeMillis,
+ minPollingTimeMillis);
return mPmicMonitor;
}
@@ -216,7 +209,7 @@
mUniqueDisplayId = uniqueDisplayId;
mDataId = dataId;
mData = PowerThrottlingData.create(data);
- mConfigData = new PowerThrottlingConfigData(0.1f, 10);
+ mConfigData = new PowerThrottlingConfigData(0.1f, 10, 20, 10);
}
@NonNull
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index 34c6ba9..1f3f19f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -50,7 +50,7 @@
}
@Test
- fun `test app request votes`(@TestParameter testCase: AppRequestTestCase) {
+ fun testAppRequestVotes(@TestParameter testCase: AppRequestTestCase) {
whenever(mockFlags.ignoreAppPreferredRefreshRateRequest())
.thenReturn(testCase.ignoreRefreshRateRequest)
val displayModeDirector = DisplayModeDirector(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
index bf2edfe..3841211 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `updates summary with base mode refresh rate if not set`() {
+ fun updatesSummary_doesNotUpdateSummary_baseModeRefreshRateNotSet() {
val summary = createVotesSummary()
baseModeVote.updateSummary(summary)
@@ -48,7 +48,7 @@
}
@Test
- fun `keeps summary base mode refresh rate if set`() {
+ fun doesNotUpdateSummary_baseModeRefreshRateSet() {
val summary = createVotesSummary()
summary.appRequestBaseModeRefreshRate = OTHER_BASE_REFRESH_RATE
@@ -58,7 +58,7 @@
}
@Test
- fun `keeps summary with base mode refresh rate if vote refresh rate is negative`() {
+ fun doesNotUpdateSummary_baseModeRefreshRateNotSet_requestedRefreshRateInvalid() {
val invalidBaseModeVote = BaseModeRefreshRateVote(-10f)
val summary = createVotesSummary()
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
index 209e5a3..0a3c285 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
@@ -45,7 +45,7 @@
}
@Test
- fun `delegates update to children`() {
+ fun delegatesUpdateToChildren() {
val summary = createVotesSummary()
combinedVote.updateSummary(summary)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
index 38782c2..5b5ae65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
@@ -28,7 +28,7 @@
class DisableRefreshRateSwitchingVoteTest {
@Test
- fun `disabled refresh rate switching is not changed`(
+ fun testDisableRefreshRateSwitch_alreadyDisabled(
@TestParameter voteDisableSwitching: Boolean
) {
val summary = createVotesSummary()
@@ -41,7 +41,7 @@
}
@Test
- fun `disables refresh rate switching if requested`() {
+ fun disablesRefreshRateSwitch_notDisabled_requested() {
val summary = createVotesSummary()
val vote = DisableRefreshRateSwitchingVote(true)
@@ -51,7 +51,7 @@
}
@Test
- fun `does not disable refresh rate switching if not requested`() {
+ fun doesNotDisableRefreshRateSwitch_notDisabled_notRequested() {
val summary = createVotesSummary()
val vote = DisableRefreshRateSwitchingVote(false)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
index 9edcc32..0968edb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
@@ -37,7 +37,7 @@
}
@Test
- fun `updates minPhysicalRefreshRate if summary has less`() {
+ fun updatesMinPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 45f
@@ -47,7 +47,7 @@
}
@Test
- fun `does not update minPhysicalRefreshRate if summary has more`() {
+ fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 75f
@@ -57,7 +57,7 @@
}
@Test
- fun `updates maxPhysicalRefreshRate if summary has more`() {
+ fun updatesMaxPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxPhysicalRefreshRate = 120f
@@ -67,7 +67,7 @@
}
@Test
- fun `does not update maxPhysicalRefreshRate if summary has less`() {
+ fun doesNotUpdateMaxPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxPhysicalRefreshRate = 75f
@@ -77,7 +77,7 @@
}
@Test
- fun `updates maxRenderFrameRate if summary has more`() {
+ fun updatesMaxRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 120f
@@ -87,7 +87,7 @@
}
@Test
- fun `does not update maxRenderFrameRate if summary has less`() {
+ fun doesNotUpdateMaxRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 75f
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
index 2d65f1c..9fa1e1b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
@@ -38,7 +38,7 @@
}
@Test
- fun `updates minRenderFrameRate if summary has less`() {
+ fun updatesMinRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minRenderFrameRate = 45f
@@ -48,7 +48,7 @@
}
@Test
- fun `does not update minRenderFrameRate if summary has more`() {
+ fun doesNotUpdateMinRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minRenderFrameRate = 75f
@@ -58,7 +58,7 @@
}
@Test
- fun `updates maxRenderFrameRate if summary has more`() {
+ fun updatesMaxPRenderFrameRateWithSmallerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 120f
@@ -68,7 +68,7 @@
}
@Test
- fun `does not update maxRenderFrameRate if summary has less`() {
+ fun doesNotUpdateMaxPRenderFrameRateWithBiggerValue() {
val summary = createVotesSummary()
summary.maxRenderFrameRate = 75f
@@ -78,7 +78,7 @@
}
@Test
- fun `updates minPhysicalRefreshRate if summary has less`() {
+ fun updatesMinPhysicalRefreshRateWithBiggerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 45f
@@ -88,7 +88,7 @@
}
@Test
- fun `does not update minPhysicalRefreshRate if summary has more`() {
+ fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
val summary = createVotesSummary()
summary.minPhysicalRefreshRate = 75f
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
index dbe9e4a..be9c563 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -28,7 +28,7 @@
class RequestedRefreshRateVoteTest {
@Test
- fun `updates requestedRefreshRates`() {
+ fun testUpdatesRequestedRefreshRates() {
val refreshRate = 90f
val vote = RequestedRefreshRateVote(refreshRate)
val summary = createVotesSummary()
@@ -40,7 +40,7 @@
}
@Test
- fun `updates requestedRefreshRates with multiple refresh rates`() {
+ fun testUpdatesRequestedRefreshRates_multipleVotes() {
val refreshRate1 = 90f
val vote1 = RequestedRefreshRateVote(refreshRate1)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4fc574a..d7dcca7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -103,7 +103,7 @@
}
@Test
- fun `test low power mode`(@TestParameter testCase: LowPowerTestCase) {
+ fun testLowPowerMode(@TestParameter testCase: LowPowerTestCase) {
whenever(mockFlags.isVsyncLowPowerVoteEnabled).thenReturn(testCase.vsyncLowPowerVoteEnabled)
whenever(spyContext.contentResolver)
.thenReturn(settingsProviderRule.mockContentResolver(null))
@@ -151,7 +151,7 @@
}
@Test
- fun `test settings refresh rates`(@TestParameter testCase: SettingsRefreshRateTestCase) {
+ fun testSettingsRefreshRates(@TestParameter testCase: SettingsRefreshRateTestCase) {
whenever(mockFlags.isPeakRefreshRatePhysicalLimitEnabled)
.thenReturn(testCase.peakRefreshRatePhysicalLimitEnabled)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
index 1be2fbf..319c21e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `updates size if width and height not set and display resolution voting disabled`() {
+ fun updatesSize_widthAndHeightNotSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = Vote.INVALID_SIZE
summary.height = Vote.INVALID_SIZE
@@ -55,7 +55,7 @@
}
@Test
- fun `does not update size if width set and display resolution voting disabled`() {
+ fun doesNotUpdateSiz_widthSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = 150
summary.height = Vote.INVALID_SIZE
@@ -71,7 +71,7 @@
}
@Test
- fun `does not update size if height set and display resolution voting disabled`() {
+ fun doesNotUpdateSize_heightSet_resolutionVotingDisabled() {
val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
summary.width = Vote.INVALID_SIZE
summary.height = 250
@@ -87,7 +87,7 @@
}
@Test
- fun `updates width if summary has more and display resolution voting enabled`() {
+ fun updatesWidthWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 850
@@ -97,7 +97,7 @@
}
@Test
- fun `does not update width if summary has less and display resolution voting enabled`() {
+ fun doesNotUpdateWidthWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 750
@@ -107,7 +107,7 @@
}
@Test
- fun `updates height if summary has more and display resolution voting enabled`() {
+ fun updatesHeightWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.height = 1650
@@ -117,7 +117,7 @@
}
@Test
- fun `does not update height if summary has less and display resolution voting enabled`() {
+ fun doesNotUpdateHeightWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.height = 1550
@@ -127,7 +127,7 @@
}
@Test
- fun `updates minWidth if summary has less and display resolution voting enabled`() {
+ fun updatesMinWidthWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minWidth = 350
@@ -138,7 +138,7 @@
}
@Test
- fun `does not update minWidth if summary has more and display resolution voting enabled`() {
+ fun doesNotUpdateMinWidthWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minWidth = 450
@@ -149,7 +149,7 @@
}
@Test
- fun `updates minHeight if summary has less and display resolution voting enabled`() {
+ fun updatesMinHeightWithSmallerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minHeight = 1150
@@ -160,7 +160,7 @@
}
@Test
- fun `does not update minHeight if summary has more and display resolution voting enabled`() {
+ fun doesNotUpdateMinHeightWithBiggerValue_resolutionVotingEnabled() {
val summary = createVotesSummary()
summary.width = 150
summary.minHeight = 1250
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
index 6ce49b8..2a50a33 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `adds supported mode ids if supportedModeIds in summary is null`() {
+ fun addsSupportedModeIds_summaryHasNull() {
val summary = createVotesSummary()
supportedModesVote.updateSummary(summary)
@@ -48,7 +48,7 @@
}
@Test
- fun `does not add supported mode ids if summary has empty list of modeIds`() {
+ fun doesNotAddSupportedModeIdes_summaryHasEmptyList() {
val summary = createVotesSummary()
summary.supportedModeIds = ArrayList()
@@ -58,7 +58,7 @@
}
@Test
- fun `filters out modes that does not match vote`() {
+ fun filtersModeIdsThatDoesNotMatchVote() {
val summary = createVotesSummary()
summary.supportedModeIds = ArrayList(listOf(otherMode, supportedModes[0]))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
index d0c112b..0da6885 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
@@ -42,7 +42,7 @@
}
@Test
- fun `adds supported refresh rates if supportedModes in summary is null`() {
+ fun addsSupportedRefreshRates_summaryHasNull() {
val summary = createVotesSummary()
supportedRefreshRatesVote.updateSummary(summary)
@@ -51,7 +51,7 @@
}
@Test
- fun `does not add supported refresh rates if summary has empty list of refresh rates`() {
+ fun doesNotAddSupportedRefreshRates_summaryHasEmptyList() {
val summary = createVotesSummary()
summary.supportedRefreshRates = ArrayList()
@@ -61,7 +61,7 @@
}
@Test
- fun `filters out supported refresh rates that does not match vote`() {
+ fun filtersSupportedRefreshRatesThatDoesNotMatchVote() {
val summary = createVotesSummary()
summary.supportedRefreshRates = ArrayList(listOf(otherMode, refreshRates[0]))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
index 5cd3a33..b2d83d7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt
@@ -41,7 +41,7 @@
private val mockConfig = mock<DisplayDeviceConfig>()
@Test
- fun `test app supported modes`(@TestParameter testCase: AppSupportedModesTestCase) {
+ fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) {
whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled)
whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
val syntheticModeManager = SyntheticModeManager(mockFlags)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
index c49205b..9ea7ea7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -51,7 +51,7 @@
private val storage = VotesStorage({}, null)
@Test
- fun `requestDisplayModes adds vote to storage`() {
+ fun testRequestDisplayModes_voteAdded() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -69,7 +69,7 @@
}
@Test
- fun `requestDisplayModes overrides votes in storage`() {
+ fun testRequestDisplayModes_voteReplaced() {
val systemRequestObserver = SystemRequestObserver(storage)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, intArrayOf(1, 2, 3))
@@ -89,7 +89,7 @@
}
@Test
- fun `requestDisplayModes removes vote to storage`() {
+ fun testRequestDisplayModes_voteRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -101,7 +101,7 @@
}
@Test
- fun `requestDisplayModes calls linkToDeath to token`() {
+ fun testTokenLinkToDeath() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -111,7 +111,7 @@
}
@Test
- fun `does not add votes to storage if binder died when requestDisplayModes called`() {
+ fun testBinderDied_voteRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -123,7 +123,7 @@
}
@Test
- fun `removes all votes from storage when binder dies`() {
+ fun testBinderDied_allVotesRemoved() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -138,7 +138,7 @@
}
@Test
- fun `calls unlinkToDeath on token when no votes remaining`() {
+ fun testTokenUnlinkToDeath_noMoreVotes() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -149,7 +149,7 @@
}
@Test
- fun `does not call unlinkToDeath on token when votes for other display in storage`() {
+ fun testTokenUnlinkToDeathNotCalled_votesForOtherDisplayInStorage() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
@@ -161,7 +161,7 @@
}
@Test
- fun `requestDisplayModes subset modes from different tokens`() {
+ fun testRequestDisplayModes_differentToken_voteHasModesSubset() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
@@ -187,7 +187,7 @@
}
@Test
- fun `recalculates vote if one binder dies`() {
+ fun testBinderDies_recalculatesVotes() {
val systemRequestObserver = SystemRequestObserver(storage)
val requestedModes = intArrayOf(1, 2, 3)
systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index dd5e1be..239e59b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -80,7 +80,7 @@
}
@Test
- fun `filters modes for summary supportedRefreshRates`(
+ fun testFiltersModes_supportedRefreshRates(
@TestParameter testCase: SupportedRefreshRatesTestCase
) {
val summary = createSummary(testCase.supportedModesVoteEnabled)
@@ -142,9 +142,7 @@
}
@Test
- fun `filters modes for summary supportedModes`(
- @TestParameter testCase: SupportedModesTestCase
- ) {
+ fun testFiltersModes_supportedModes(@TestParameter testCase: SupportedModesTestCase) {
val summary = createSummary(testCase.supportedModesVoteEnabled)
summary.supportedModeIds = testCase.summarySupportedModes
@@ -154,7 +152,7 @@
}
@Test
- fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+ fun testInvalidSummary_requestedRefreshRateLessThanMinRenderRate() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(30f, 90f)
summary.minRenderFrameRate = 60f
@@ -166,7 +164,7 @@
}
@Test
- fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+ fun testInvalidSummary_requestedRefreshRateMoreThanMaxRenderRate() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(60f, 240f)
summary.minRenderFrameRate = 60f
@@ -178,7 +176,7 @@
}
@Test
- fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+ fun testValidSummary_requestedRefreshRatesWithingRenderRateLimits() {
val summary = createSummary()
summary.requestedRefreshRates = setOf(60f, 90f)
summary.minRenderFrameRate = 60f
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index 1c4db6a..c1d7c7b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.PowerManager;
+import android.os.Process;
import org.junit.Before;
import org.junit.Test;
@@ -54,6 +55,8 @@
when(mPackageManager.getPackagesForUid(101)).thenReturn(new String[]{ "some.package1" });
when(mPackageManager.getPackagesForUid(102)).thenReturn(new String[]{ "some.package2" });
+ when(mPackageManager.getPackagesForUid(Process.SYSTEM_UID))
+ .thenReturn(new String[]{ "some.package3" });
}
@Test
@@ -70,14 +73,20 @@
log.onWakeLockAcquired("TagFull", 102,
PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1250L);
+ log.onWakeLockAcquired("TagSystem", 1000,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ "(partial,on-after-release)\n"
+ " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
+ "(full,acq-causes-wake)\n"
+ + " 01-01 00:00:01.250 - 1000 (" + WakeLockLog.SYSTEM_PACKAGE_NAME + ")"
+ + " - ACQ TagSystem (full,acq-causes-wake)\n"
+ " -\n"
- + " Events: 2, Time-Resets: 0\n"
- + " Buffer, Bytes used: 6\n",
+ + " Events: 3, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 9\n",
dumpLog(log, false));
}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 1db46bf..2a55521 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -55,6 +55,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -145,7 +146,6 @@
*/
@SmallTest
@Presubmit
-
public class UserControllerTest {
// Use big enough user id to avoid picking up already active user id.
private static final int TEST_USER_ID = 100;
@@ -593,6 +593,7 @@
@Test
public void testScheduleStopOfBackgroundUser_switch() {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -642,6 +643,7 @@
@Test
public void testScheduleStopOfBackgroundUser_startInBackground() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -681,6 +683,7 @@
@Test
public void testScheduleStopOfBackgroundUser_rescheduleWhenGuest() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -736,6 +739,7 @@
@Test
public void testScheduleStopOfBackgroundUser_rescheduleIfAlarm() throws Exception {
mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+ assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 5a78d9e..1a593dd 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -37,7 +37,6 @@
import android.app.WindowConfiguration;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
-import android.companion.virtual.VirtualDeviceManager;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -94,15 +93,9 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
- private VirtualDeviceManager.ActivityListener mActivityListener;
- @Mock
- private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback;
- @Mock
- private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback;
+ private GenericWindowPolicyController.ActivityListener mActivityListener;
@Mock
private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener;
- @Mock
- private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback;
@Before
public void setUp() throws Exception {
@@ -669,14 +662,14 @@
/* targetDisplayCategory */ null);
// register interceptor and intercept intent
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
.isFalse();
// unregister interceptor and launch activity
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
@@ -696,13 +689,12 @@
/* targetDisplayCategory */ null);
// register interceptor with different filter
- when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
/* isResultExpected = */ false, /* intentSender= */ null))
.isTrue();
- verify(mIntentListenerCallback, timeout(TIMEOUT_MILLIS))
- .shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS)).shouldInterceptIntent(any(Intent.class));
}
@Test
@@ -723,8 +715,8 @@
/* isResultExpected = */ true, /* intentSender= */ () -> intentSender))
.isFalse();
- verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
- .onActivityBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onActivityLaunchBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
}
@Test
@@ -761,10 +753,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
- verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
- .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -780,10 +772,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
- verify(mSecureWindowCallback, timeout(TIMEOUT_MILLIS)).onSecureWindowShown(DISPLAY_ID,
- activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -800,10 +792,10 @@
assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
- verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
- .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
- verify(mActivityBlockedCallback, never())
- .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never())
+ .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@Test
@@ -835,9 +827,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ mSecureWindowCallback,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -855,9 +844,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ mSecureWindowCallback,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -876,9 +862,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ homeComponent);
@@ -897,9 +880,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -918,9 +898,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -939,9 +916,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ Collections.singleton(displayCategory),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -960,9 +934,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -981,9 +952,6 @@
/* crossTaskNavigationAllowedByDefault= */ false,
/* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
/* activityListener= */ mActivityListener,
- /* activityBlockedCallback= */ mActivityBlockedCallback,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ mIntentListenerCallback,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
@@ -1029,9 +997,9 @@
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask, /* isResultExpected= */ false, () -> intentSender)).isTrue();
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(fromDisplay, activityInfo, intentSender);
- verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+ verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
}
private void assertActivityIsBlocked(GenericWindowPolicyController gwpc,
@@ -1046,9 +1014,9 @@
assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
isNewTask, /* isResultExpected= */ false, () -> intentSender)).isFalse();
- verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
- .onActivityBlocked(fromDisplay, activityInfo, intentSender);
- verify(mIntentListenerCallback, after(TIMEOUT_MILLIS).never())
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+ .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.shouldInterceptIntent(any(Intent.class));
}
@@ -1060,8 +1028,8 @@
/* isResultExpected= */ false, () -> intentSender))
.isFalse();
- verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
- .onActivityBlocked(eq(fromDisplay), eq(activityInfo), any());
- verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+ .onActivityLaunchBlocked(eq(fromDisplay), eq(activityInfo), any());
+ verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 405929a..51c2ad1 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -87,9 +87,6 @@
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
/* activityListener= */ null,
- /* activityBlockedCallback= */ null,
- /* secureWindowCallback= */ null,
- /* intentListenerCallback= */ null,
/* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true,
/* customHomeComponent= */ null);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index b2fd8aa..161b18c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -165,7 +165,7 @@
assertTrue(resultContains(
callShellCommand("reset-throttling", "--user", "10"),
- "User 10 is not running or locked"));
+ "User (with userId=10) is not running or locked"));
mRunningUsers.put(USER_10, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0a52238..6a1140c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -14995,7 +14995,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_acceptsCorrectToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15014,7 +15013,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_acceptsNullToken_andPopulatesIt() throws RemoteException {
Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15031,7 +15029,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15054,7 +15051,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_rejectsOtherToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15072,7 +15068,6 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_customParcelingWithFakeInnerToken_hasCorrectTokenInIntents()
throws RemoteException {
Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -15278,7 +15273,6 @@
@Test
@SuppressWarnings("unchecked")
- @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void getActiveNotifications_doesNotLeakAllowlistToken() throws RemoteException {
Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index e70ed5f..f8ff1f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -788,6 +788,19 @@
}
@Test
+ public void testRuleXml_invalidInterruptionFilter_readsDefault() throws Exception {
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.zenMode = 1979;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(rule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertThat(fromXml.zenMode).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ }
+
+ @Test
public void testZenPolicyXml_allUnset() throws Exception {
ZenPolicy policy = new ZenPolicy.Builder().build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index c1e3f47..baa633f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,6 +18,8 @@
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
@@ -6875,6 +6877,52 @@
"Didn't find rule with id %s", ruleId);
}
+ @Test
+ @DisableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testDefaultConfig_preModesApi_rulesAreBare() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(101);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isNull();
+ assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+ assertThat(eventsRule.triggerDescription).isNull();
+ }
+
+ @Test
+ @EnableFlags(FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_UI)
+ public void testDefaultConfig_modesApi_rulesHaveFullPolicy() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(201);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+ assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+ assertThat(eventsRule.triggerDescription).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void testDefaultConfig_modesUi_rulesHaveFullPolicy() {
+ // Create a new user, which should get a copy of the default policy.
+ mZenModeHelper.onUserSwitched(301);
+
+ ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+ ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+ assertThat(eventsRule).isNotNull();
+ assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+ assertThat(eventsRule.type).isEqualTo(TYPE_SCHEDULE_CALENDAR);
+ assertThat(eventsRule.triggerDescription).isNotEmpty();
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 6e48818..3910904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
@@ -25,9 +26,11 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +46,9 @@
import android.content.pm.PackageManagerInternal;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.DeviceConfig;
import android.util.Pair;
@@ -52,6 +58,7 @@
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.am.PendingIntentRecord;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -95,7 +102,9 @@
@Rule
public final ExtendedMockitoRule extendedMockitoRule =
new ExtendedMockitoRule.Builder(this).setStrictness(Strictness.LENIENT).build();
-
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
TestableBackgroundActivityStartController mController;
@Mock
ActivityMetricsLogger mActivityMetricsLogger;
@@ -186,7 +195,7 @@
when(mAppOpsManager.checkOpNoThrow(
eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
}
@@ -227,7 +236,7 @@
// call
BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
balState);
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForCaller(callerVerdict);
@@ -295,7 +304,77 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForRealCaller(realCallerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_VISIBLE_WINDOW);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+ balState);
+ balState.setResultForCaller(callerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+ BAL_ALLOW_VISIBLE_WINDOW);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testRealCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true);
+ when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -320,7 +399,7 @@
int realCallingPid = REGULAR_PID_2;
// setup state
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
@@ -357,7 +436,7 @@
mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
mCallerApp);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
// prepare call
@@ -371,7 +450,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -404,9 +483,9 @@
mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
mCallerApp);
when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
- when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
- when(otherProcess.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ when(otherProcess.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
// prepare call
@@ -420,7 +499,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -456,7 +535,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
@@ -466,6 +545,45 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+ public void testRealCaller_isCompanionAppWithOptInIfVisible() {
+ // The app has a service that is bound by a different, visible app. The app bound to the
+ // service must remain visible for the app in the background to start activities
+ // successfully.
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = REGULAR_UID_2;
+ int realCallingPid = REGULAR_PID_2;
+
+ // setup state
+ final int realCallingUserId = UserHandle.getUserId(realCallingUid);
+ when(mService.isAssociatedCompanionApp(eq(realCallingUserId),
+ eq(realCallingUid))).thenReturn(true);
+
+ // prepare call
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = mCheckedOptions
+ .setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+ BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+ callingPid, callingPackage, realCallingUid, realCallingPid, null,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // call
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+ balState);
+ balState.setResultForRealCaller(realCallerVerdict);
+
+ // assertions
+ assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+ BAL_BLOCK);
+ }
+
+ @Test
public void testCaller_balPermission() {
int callingUid = REGULAR_UID_1;
int callingPid = REGULAR_PID_1;
@@ -523,7 +641,7 @@
checkedOptions);
// call
- BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+ BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
balState);
balState.setResultForRealCaller(realCallerVerdict);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index e364264..6ec7895 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -24,6 +24,7 @@
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.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +44,7 @@
import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.server.am.PendingIntentRecord;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
import org.junit.After;
import org.junit.Before;
@@ -167,9 +169,9 @@
}
@Override
- BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+ BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
return mRealCallerVerdict.orElseGet(
- () -> super.checkBackgroundActivityStartAllowedBySender(state));
+ () -> super.checkBackgroundActivityStartAllowedByRealCaller(state));
}
public void setRealCallerVerdict(BalVerdict verdict) {
@@ -177,11 +179,12 @@
}
@Override
- BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state) {
+ BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state,
+ BalCheckConfiguration checkConfiguration) {
if (mProcessVerdicts.containsKey(app)) {
return mProcessVerdicts.get(app);
}
- return super.checkProcessAllowsBal(app, state);
+ return super.checkProcessAllowsBal(app, state, checkConfiguration);
}
}
@@ -209,7 +212,7 @@
Mockito.when(mAppOpsManager.checkOpNoThrow(
eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
- Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+ Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
BalVerdict.BLOCK);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
index c9c7e92..27e147d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BOUND_BY_FOREGROUND;
@@ -95,7 +96,12 @@
int mUid = 234;
String mPackageName = "package.name";
int mAppSwitchState = APP_SWITCH_DISALLOW;
- boolean mIsCheckingForFgsStart = false;
+ BackgroundLaunchProcessController.BalCheckConfiguration mBalCheckConfiguration =
+ new BackgroundLaunchProcessController.BalCheckConfiguration(
+ /* isCheckingForFgsStarts */ false,
+ /* checkVisibility */ true,
+ /* checkOtherExemptions */ true,
+ ACTIVITY_BG_START_GRACE_PERIOD_MS);
boolean mHasActivityInVisibleTask = false;
boolean mHasBackgroundActivityStartPrivileges = false;
long mLastStopAppSwitchesTime = 0L;
@@ -106,7 +112,7 @@
public void testNothingAllows() {
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -118,7 +124,7 @@
mHasBackgroundActivityStartPrivileges = true;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -136,7 +142,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -154,7 +160,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -170,7 +176,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -186,7 +192,7 @@
BackgroundStartPrivileges.ALLOW_BAL);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -201,7 +207,7 @@
mHasActiveVisibleWindow.add(999);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -216,7 +222,7 @@
mHasActiveVisibleWindow.add(999);
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -229,7 +235,7 @@
mHasActivityInVisibleTask = true;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
@@ -245,7 +251,7 @@
mLastActivityFinishTime = now - 100;
BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
mPid, mUid, mPackageName,
- mAppSwitchState, mIsCheckingForFgsStart,
+ mAppSwitchState, mBalCheckConfiguration,
mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
mLastStopAppSwitchesTime, mLastActivityLaunchTime,
mLastActivityFinishTime);
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 753cb1f..3f6a0bf 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -48,6 +48,13 @@
RIGHT_BOTTOM
}
+ enum class Edges {
+ LEFT,
+ RIGHT,
+ TOP,
+ BOTTOM
+ }
+
/** Wait for an app moved to desktop to finish its transition. */
private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) {
wmHelper
@@ -124,7 +131,8 @@
val displayRect = getDisplayRect(wmHelper)
val insets = getWindowInsets(
- context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
+ context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
+ )
displayRect.inset(insets)
val expectedWidth = displayRect.width() / 2
@@ -187,6 +195,40 @@
dragWindow(startX, startY, endX, endY, wmHelper, device)
}
+ /** Resize a desktop app from its edges. */
+ fun edgeResize(
+ wmHelper: WindowManagerStateHelper,
+ motionEvent: MotionEventHelper,
+ edge: Edges
+ ) {
+ val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
+ val (startX, startY) = getStartCoordinatesForEdgeResize(windowRect, edge)
+ val verticalChange = when (edge) {
+ Edges.LEFT -> 0
+ Edges.RIGHT -> 0
+ Edges.TOP -> -100
+ Edges.BOTTOM -> 100
+ }
+ val horizontalChange = when (edge) {
+ Edges.LEFT -> -100
+ Edges.RIGHT -> 100
+ Edges.TOP -> 0
+ Edges.BOTTOM -> 0
+ }
+
+ // The position we want to drag to
+ val endY = startY + verticalChange
+ val endX = startX + horizontalChange
+
+ motionEvent.actionDown(startX, startY)
+ motionEvent.actionMove(startX, startY, endX, endY, /* steps= */100)
+ motionEvent.actionUp(endX, endY)
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .waitForAndVerify()
+ }
+
/** Drag a window from a source coordinate to a destination coordinate. */
fun dragWindow(
startX: Int, startY: Int,
@@ -237,6 +279,18 @@
}
}
+ private fun getStartCoordinatesForEdgeResize(
+ windowRect: Rect,
+ edge: Edges
+ ): Pair<Int, Int> {
+ return when (edge) {
+ Edges.LEFT -> Pair(windowRect.left, windowRect.bottom / 2)
+ Edges.RIGHT -> Pair(windowRect.right, windowRect.bottom / 2)
+ Edges.TOP -> Pair(windowRect.right / 2, windowRect.top)
+ Edges.BOTTOM -> Pair(windowRect.right / 2, windowRect.bottom)
+ }
+ }
+
/** Exit desktop mode by dragging the app handle to the top drag zone. */
fun exitDesktopWithDragToTopDragZone(
wmHelper: WindowManagerStateHelper,
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
new file mode 100644
index 0000000..0835398
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.os.SystemClock
+import android.view.ContentInfo.Source
+import android.view.InputDevice.SOURCE_MOUSE
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import android.view.MotionEvent.TOOL_TYPE_FINGER
+import android.view.MotionEvent.TOOL_TYPE_MOUSE
+import android.view.MotionEvent.TOOL_TYPE_STYLUS
+import android.view.MotionEvent.ToolType
+
+/**
+ * Helper class for injecting a custom motion event and performing some actions. This is used for
+ * instrumenting input injections like stylus, mouse and touchpad.
+ */
+class MotionEventHelper(
+ private val instr: Instrumentation,
+ private val inputMethod: InputMethod
+) {
+ enum class InputMethod(@ToolType val toolType: Int, @Source val source: Int) {
+ STYLUS(TOOL_TYPE_STYLUS, SOURCE_STYLUS),
+ MOUSE(TOOL_TYPE_MOUSE, SOURCE_MOUSE),
+ TOUCHPAD(TOOL_TYPE_FINGER, SOURCE_MOUSE)
+ }
+
+ fun actionDown(x: Int, y: Int) {
+ injectMotionEvent(ACTION_DOWN, x, y)
+ }
+
+ fun actionUp(x: Int, y: Int) {
+ injectMotionEvent(ACTION_UP, x, y)
+ }
+
+ fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int) {
+ val incrementX = (endX - startX).toFloat() / (steps - 1)
+ val incrementY = (endY - startY).toFloat() / (steps - 1)
+
+ for (i in 0..steps) {
+ val time = SystemClock.uptimeMillis()
+ val x = startX + incrementX * i
+ val y = startY + incrementY * i
+
+ val moveEvent = getMotionEvent(time, time, ACTION_MOVE, x, y)
+ injectMotionEvent(moveEvent)
+ }
+ }
+
+ private fun injectMotionEvent(action: Int, x: Int, y: Int): MotionEvent {
+ val eventTime = SystemClock.uptimeMillis()
+ val event = getMotionEvent(eventTime, eventTime, action, x.toFloat(), y.toFloat())
+ injectMotionEvent(event)
+ return event
+ }
+
+ private fun injectMotionEvent(event: MotionEvent) {
+ instr.uiAutomation.injectInputEvent(event, true, false)
+ }
+
+ private fun getMotionEvent(
+ downTime: Long,
+ eventTime: Long,
+ action: Int,
+ x: Float,
+ y: Float,
+ ): MotionEvent {
+ val properties = MotionEvent.PointerProperties.createArray(1)
+ properties[0].toolType = inputMethod.toolType
+ properties[0].id = 1
+
+ val coords = MotionEvent.PointerCoords.createArray(1)
+ coords[0].x = x
+ coords[0].y = y
+ coords[0].pressure = 1f
+
+ val event =
+ MotionEvent.obtain(
+ downTime,
+ eventTime,
+ action,
+ /* pointerCount= */ 1,
+ properties,
+ coords,
+ /* metaState= */ 0,
+ /* buttonState= */ 0,
+ /* xPrecision = */ 1f,
+ /* yPrecision = */ 1f,
+ /* deviceId = */ 0,
+ /* edgeFlags = */ 0,
+ inputMethod.source,
+ /* flags = */ 0
+ )
+ event.displayId = 0
+ return event
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 43fd57b..931e4f8 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -269,9 +269,23 @@
/** Expand the PIP window back to full screen via intent and wait until the app is visible */
fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
- fun changeAspectRatio() {
+ fun changeAspectRatio(wmHelper: WindowManagerStateHelper) {
val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
context.sendBroadcast(intent)
+ // Wait on WMHelper on size change upon aspect ratio change
+ val windowRect = getWindowRect(wmHelper)
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipAspectRatioChanged") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegion = pipAppWindow.frameRegion
+ return@add pipRegion != Region(windowRect)
+ }
+ .waitForAndVerify()
}
fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 06c2651..65398a2 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -40,7 +40,7 @@
"frameworks-base-testutils",
"hamcrest-library",
"kotlin-test",
- "mockito-target-minus-junit4",
+ "mockito-target-extended-minus-junit4",
"platform-test-annotations",
"platform-screenshot-diff-core",
"services.core.unboosted",