Merge "Clean up fully rolled out DevicePolicySizeTrackingInternalBugFixEnabled" 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 3637ca7..e7ed8fb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -61,6 +61,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE";
field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
+ field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE";
field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
@@ -4631,6 +4632,7 @@
method public int getCommittedSessionId();
method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
method public int getRollbackId();
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public int getRollbackImpactLevel();
method public boolean isStaged();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bf8cc20..cc41302 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1269,10 +1269,6 @@
package android.content.rollback {
- public final class RollbackInfo implements android.os.Parcelable {
- method @FlaggedApi("android.content.pm.recoverability_detection") public int getRollbackImpactLevel();
- }
-
public final class RollbackManager {
method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long);
method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
@@ -1801,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/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/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 39e041b..bf90912 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
@@ -236,13 +237,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"
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/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index d128055..a20159d 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -19,8 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Parcel;
@@ -136,11 +134,8 @@
* Get rollback impact level. Refer {@link
* android.content.pm.PackageInstaller.SessionParams#setRollbackImpactLevel(int)} for more info
* on impact level.
- *
- * @hide
*/
- @TestApi
- @FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
public @PackageManager.RollbackImpactLevel int getRollbackImpactLevel() {
return mRollbackImpactLevel;
}
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/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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 17ff2eb..5decf7f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7648,7 +7648,8 @@
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+ <!-- @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY) @SystemApi
+ Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
ensure that only the system can bind to it.
@hide This is not a third-party API (intended for OEMs and system apps).
-->
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/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/Android.bp b/libs/WindowManager/Shell/Android.bp
index b338a2a..a79bc97 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,17 +39,6 @@
path: "src",
}
-// Sources that have no dependencies that can be used directly downstream of this library
-// TODO(b/322791067): move these sources to WindowManager-Shell-shared
-filegroup {
- name: "wm_shell_util-sources",
- srcs: [
- "src/com/android/wm/shell/common/bubbles/*.kt",
- "src/com/android/wm/shell/common/bubbles/*.java",
- ],
- path: "src",
-}
-
// Aidls which can be used directly downstream of this library
filegroup {
name: "wm_shell-aidls",
@@ -184,9 +173,11 @@
":wm_shell-shared-aidls",
],
static_libs: [
+ "androidx.core_core-animation",
"androidx.dynamicanimation_dynamicanimation",
"jsr330",
],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
@@ -212,7 +203,6 @@
],
static_libs: [
"androidx.appcompat_appcompat",
- "androidx.core_core-animation",
"androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx.datastore_datastore",
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
index d35f493..f09969d 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles
import android.view.LayoutInflater
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupView
import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
import com.android.wm.shell.R
import org.junit.Rule
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 4b97451..b38d00da 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -30,7 +30,7 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index faadf1d..96ffa03 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -53,7 +53,7 @@
import org.mockito.kotlin.mock
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 935d129..ecb2b25 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -31,12 +31,12 @@
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.DeviceConfig
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index a0a06f1..806d026 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
android:textAlignment="center"
android:text="@string/bubble_bar_education_manage_text"/>
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index b489a5c..7fa586c 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
android:textAlignment="center"
android:text="@string/bubble_bar_education_stack_text"/>
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
index eec2468..7086691 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.Point
import android.graphics.RectF
@@ -23,9 +23,9 @@
import androidx.core.animation.Animator
import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.ObjectAnimator
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
/**
* Base class for common logic shared between different bubble views to support pinning bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
similarity index 93%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
index 3c5beeb..4fe7611 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
parcelable BubbleBarLocation;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
index f0bdfde..191875d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
index ec3c601..5bde1e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
@@ -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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
similarity index 89%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
index 0329b8d..3396bc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
@@ -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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
/**
* Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
index e873cbd..5876682 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
index 887af17..8681acf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.annotation.ColorInt
import android.graphics.Canvas
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
index 444fbf7..802d7d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.graphics.Rect
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
index 7c5bb21..0c05156 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
index e06de9e..2bb66b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.animation.ObjectAnimator
import android.content.Context
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
index 4e55ba2..b1f4e33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.graphics.PointF
import android.view.MotionEvent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
similarity index 94%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
index f90591b..c83696c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
@@ -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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.wm.shell.shared.bubbles;
import android.annotation.NonNull;
import android.os.Parcel;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 4622dcf..0c95934 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -53,9 +53,9 @@
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import java.io.PrintWriter;
import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index b508c1b..c545d73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -104,14 +104,14 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 4ad1802..709a7bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -41,10 +41,10 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
-import com.android.wm.shell.common.bubbles.RemovedBubble;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.RemovedBubble;
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 4e80e90..ec4854b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/** Manager interface for bubble expanded views. */
interface BubbleExpandedViewManager {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
index bdb09e1..fd110a276 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -17,8 +17,8 @@
import android.graphics.Color
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
/**
* A convenience method to setup the [BubblePopupView] with the correct config using local resources
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 0cf187b..c386c93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -32,7 +32,7 @@
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Keeps track of display size, configuration, and specific bubble sizes. One place for all
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 53bbf88..2795881 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -24,10 +24,10 @@
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT;
import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -91,8 +91,8 @@
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.bubbles.RelativeTouchListener;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 9a27fb6..62895fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -38,9 +38,9 @@
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
index 48692d4..00a8172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -18,7 +18,7 @@
package com.android.wm.shell.bubbles
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.DismissView
fun DismissView.setup() {
setup(DismissView.Config(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 5779a8f..1855b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -20,7 +20,7 @@
import android.graphics.Rect;
import android.content.pm.ShortcutInfo;
import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index 14d29cd..eb907db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -17,7 +17,7 @@
package com.android.wm.shell.bubbles;
import android.os.Bundle;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
/**
* Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
*/
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 6d868d2..694b1b0 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
@@ -46,7 +46,7 @@
import com.android.wm.shell.bubbles.BubbleTaskView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.taskview.TaskView;
import java.util.function.Supplier;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index eeb5c94..07463bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -20,8 +20,8 @@
import android.view.MotionEvent
import android.view.View
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.DismissView
-import com.android.wm.shell.common.bubbles.RelativeTouchListener
+import com.android.wm.shell.shared.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index ac42453..1c9c195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -44,9 +44,9 @@
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
-import com.android.wm.shell.common.bubbles.BaseBubblePinController;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DismissView;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index e108f7b..9fd255d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -34,9 +34,9 @@
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
import kotlin.math.roundToInt
/** Manages bubble education presentation and animation */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
index 651bf02..23ba2bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
@@ -25,8 +25,8 @@
import androidx.core.view.updateLayoutParams
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/**
* Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
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/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
index 0acc7df..faa97ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -98,9 +98,8 @@
### Exposing shared code for use in Launcher
Launcher doesn't currently build against the Shell library, but needs to have access to some shared
AIDL interfaces and constants. Currently, all AIDL files, and classes under the
-`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
+`com.android.wm.shell.shared` package are automatically built into the `SystemUISharedLib` that
Launcher uses.
-If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
-[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the
-`wm_shell_util-sources` filegroup.
\ No newline at end of file
+If the new code doesn't fall into those categories, they should be moved to the Shell shared
+package (`com.android.wm.shell.shared`) under the `WindowManager-Shell-shared` library.
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 0d2b8e7..06d2311 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -35,9 +35,9 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index e04178e..b3070f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -35,9 +35,9 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import kotlin.Unit;
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/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/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/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 859602e..6fa3788 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -50,8 +50,8 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.bubbles.BubbleData.TimeSource;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.google.common.collect.ImmutableList;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index 50c4a18..dca5fc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -43,7 +43,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
index 27e0b19..b9bf95b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.DEFAULT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
index 6695a1e..641063c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles
+package com.android.wm.shell.shared.bubbles
import android.os.Parcel
import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
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/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
similarity index 84%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
copy to packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
index 3c5beeb..1726036 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell.common.bubbles;
+package com.android.settingslib.bluetooth.devicesettings;
-parcelable BubbleBarLocation;
\ No newline at end of file
+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/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 1ce1716..2e0c1ef 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1373,6 +1373,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/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
copy to packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
index 06905379..bc4adf9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.shade.shared.model
+package com.android.systemui.scene
-/** Enumerates all supported alignments of the shade. */
-sealed interface ShadeAlignment {
+import com.android.systemui.qs.ui.composable.QuickSettingsShadeOverlay
+import com.android.systemui.scene.ui.composable.Overlay
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
- /** Aligns the shade to the top. */
- data object Top : ShadeAlignment
+@Module
+interface QuickSettingsShadeOverlayModule {
- /** Aligns the shade to the bottom. */
- data object Bottom : ShadeAlignment
+ @Binds @IntoSet fun quickSettingsShade(overlay: QuickSettingsShadeOverlay): Overlay
}
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..8c159c5 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,7 +27,6 @@
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
@@ -37,14 +36,12 @@
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,7 +57,6 @@
private val statusBarIconController: StatusBarIconController,
private val shadeSession: SaveableSession,
private val stackScrollView: Lazy<NotificationScrollView>,
- private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
) : ExclusiveActivatable(), ComposableScene {
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/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
new file mode 100644
index 0000000..988c712
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.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
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+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 javax.inject.Inject
+
+@SysUISingleton
+class QuickSettingsShadeOverlay
+@Inject
+constructor(
+ private val actionsViewModelFactory: QuickSettingsShadeOverlayActionsViewModel.Factory,
+ private val contentViewModelFactory: QuickSettingsShadeOverlayContentViewModel.Factory,
+ private val tintedIconManagerFactory: TintedIconManager.Factory,
+ private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+ private val statusBarIconController: StatusBarIconController,
+) : Overlay {
+
+ override val key = Overlays.QuickSettingsShade
+
+ private val actionsViewModel: QuickSettingsShadeOverlayActionsViewModel by lazy {
+ actionsViewModelFactory.create()
+ }
+
+ override suspend fun activate(): Nothing {
+ actionsViewModel.activate()
+ }
+
+ @Composable
+ override fun ContentScope.Content(
+ modifier: Modifier,
+ ) {
+ val viewModel =
+ rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
+
+ OverlayShade(
+ modifier = modifier,
+ onScrimClicked = viewModel::onScrimClicked,
+ ) {
+ Column {
+ ExpandedShadeHeader(
+ viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
+ )
+
+ ShadeBody(
+ viewModel = viewModel.quickSettingsContainerViewModel,
+ )
+ }
+ }
+ }
+}
+
+@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..9769205 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,40 +16,18 @@
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
@@ -59,8 +37,6 @@
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,7 +46,6 @@
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,
@@ -96,10 +71,10 @@
) {
val viewModel =
rememberViewModel("QuickSettingsShadeScene") { contentViewModelFactory.create() }
+
OverlayShade(
- viewModelFactory = viewModel.overlayShadeViewModelFactory,
- lockscreenContent = lockscreenContent,
modifier = modifier,
+ onScrimClicked = {},
) {
Column {
ExpandedShadeHeader(
@@ -117,68 +92,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/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/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/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..4b36768 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]. */
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 99a9777..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
@@ -285,8 +293,9 @@
)
suspend fun run() {
- // When this animation is started, wait for the offset animation runnable to be set and
- // run it.
+ // This animation will first be driven by finger, then when the user lift their finger we
+ // start an animation to the target offset (progress = 1f or progress = 0f). We await() for
+ // offsetAnimationRunnable to be completed and then run it.
val runAnimation = offsetAnimationRunnable.await() ?: return
runAnimation()
}
@@ -347,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/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/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 29eedf6..8befad1 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
@@ -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, coroutineScope = this)
+
+ // The previous job is cancelled and does not infinitely collect the progress.
+ job.join()
+ }
}
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/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/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index d7acaaf..80de087 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import org.junit.Before;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 4373c88..46f076a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -46,7 +46,7 @@
import com.android.systemui.accessibility.MotionEventHelper;
import com.android.systemui.accessibility.utils.TestUtils;
import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import org.junit.After;
import org.junit.Before;
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
new file mode 100644
index 0000000..762941d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+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.lifecycle.activateIn
+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 QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val underTest = kosmos.quickSettingsShadeOverlayActionsViewModel
+
+ @Test
+ fun upTransitionSceneKey_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.QuickSettingsShade)
+ assertThat(actions?.get(Swipe.Down)).isNull()
+ }
+
+ @Test
+ fun back_hidesShade() =
+ testScope.runTest {
+ val actions by collectLastValue(underTest.actions)
+ underTest.activateIn(this)
+
+ assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+ .isEqualTo(Overlays.QuickSettingsShade)
+ }
+}
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/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index e68da09..8f55961 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -49,7 +49,6 @@
"src/**/*.aidl",
":wm_shell-aidls",
":wm_shell-shared-aidls",
- ":wm_shell_util-sources",
],
static_libs: [
"BiometricsSharedLib",
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/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index d718ae3..708f7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -30,8 +30,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Flags;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import java.util.Map;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
index c1b3962..13c1a45 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -39,9 +39,9 @@
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissCircleView
-import com.android.wm.shell.common.bubbles.DismissView
import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.DismissCircleView
+import com.android.wm.shell.shared.bubbles.DismissView
/**
* View that handles interactions between DismissCircleView and BubbleStackView.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index d62162b..7a674e2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -81,7 +81,7 @@
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.bubbles.DismissViewUtils;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
import java.lang.annotation.Retention;
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/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
new file mode 100644
index 0000000..9538392
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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 com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+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.ui.viewmodel.SceneActionsViewModel
+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() :
+ SceneActionsViewModel() {
+
+ override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+ setActions(
+ buildMap {
+ put(Swipe.Up, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+ put(Back, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+ }
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsShadeOverlayActionsViewModel
+ }
+}
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
new file mode 100644
index 0000000..3b97d82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * 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 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
+
+/**
+ * Models UI state used to render the content of the quick settings shade overlay.
+ *
+ * Different from [QuickSettingsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class QuickSettingsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+ val sceneInteractor: SceneInteractor,
+ val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+ val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+) {
+ fun onScrimClicked() {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = "Shade scrim clicked",
+ )
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): QuickSettingsShadeOverlayContentViewModel
+ }
+}
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/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 98cf941..00944b8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -46,6 +46,7 @@
NotificationsShadeOverlayModule::class,
NotificationsShadeSceneModule::class,
NotificationsShadeSessionModule::class,
+ QuickSettingsShadeOverlayModule::class,
QuickSettingsSceneModule::class,
ShadeSceneModule::class,
SceneDomainModule::class,
@@ -104,6 +105,7 @@
overlayKeys =
listOfNotNull(
Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+ Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
),
navigationDistances =
mapOf(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 8fc896c..4061ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -48,6 +48,7 @@
LockscreenSceneModule::class,
QuickSettingsSceneModule::class,
ShadeSceneModule::class,
+ QuickSettingsShadeOverlayModule::class,
QuickSettingsShadeSceneModule::class,
NotificationsShadeOverlayModule::class,
NotificationsShadeSceneModule::class,
@@ -111,6 +112,7 @@
overlayKeys =
listOfNotNull(
Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+ Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
),
navigationDistances =
mapOf(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
index 0bb02e9..c47a850 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
@@ -36,4 +36,17 @@
* side-by-side in their own columns).
*/
@JvmField val NotificationsShade = OverlayKey("notifications_shade")
+
+ /**
+ * The quick settings shade overlay shows the quick settings tiles UI.
+ *
+ * It's used only in the dual shade configuration, where there are two separate shades: one for
+ * quick settings (this overlay) and another for [NotificationsShade].
+ *
+ * It's not used in the single/accordion configuration (swipe down once to reveal the shade,
+ * swipe down again the to expand quick settings) or in the "split" shade configuration (on
+ * large screens or unfolded foldables, where notifications and quick settings are shown
+ * side-by-side in their own columns).
+ */
+ @JvmField val QuickSettingsShade = OverlayKey("quick_settings_shade")
}
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/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/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/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..aa54d51 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
@@ -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
}
}
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/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 925ebf3..036e21c 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
@@ -2629,6 +2629,9 @@
}
private void updateScrollability() {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
if (scrollable != mScrollable) {
mScrollable = scrollable;
@@ -2638,6 +2641,7 @@
}
private void updateForwardAndBackwardScrollability() {
+ SceneContainerFlag.assertInLegacyMode();
boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom();
boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop();
boolean changed = forwardScrollable != mForwardScrollable
@@ -5147,10 +5151,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 +5200,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/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..7de088d 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 =
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/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/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e39365..3e7980d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -184,11 +184,11 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
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/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
similarity index 66%
rename from packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
index 06905379..8fc40e4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.shade.shared.model
+package com.android.systemui.qs.ui.viewmodel
-/** Enumerates all supported alignments of the shade. */
-sealed interface ShadeAlignment {
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
- /** Aligns the shade to the top. */
- data object Top : ShadeAlignment
-
- /** Aligns the shade to the bottom. */
- data object Bottom : ShadeAlignment
+val Kosmos.quickSettingsShadeOverlayActionsViewModel:
+ QuickSettingsShadeOverlayActionsViewModel by Fixture {
+ QuickSettingsShadeOverlayActionsViewModel()
}
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/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
similarity index 60%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index 00f1526..ff8b478 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -14,25 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.shade.ui.viewmodel
+package com.android.systemui.qs.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
-val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
+val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayContentViewModel by
Kosmos.Fixture {
- OverlayShadeViewModel(
+ QuickSettingsShadeOverlayContentViewModel(
sceneInteractor = sceneInteractor,
- shadeInteractor = shadeInteractor,
+ shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
+ quickSettingsContainerViewModel = quickSettingsContainerViewModel,
)
}
-
-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/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 7bc2483..dc45d93 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
@@ -28,6 +28,7 @@
var Kosmos.overlayKeys by Fixture {
listOf(
Overlays.NotificationsShade,
+ Overlays.QuickSettingsShade,
)
}
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/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/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/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..277d582 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -48,7 +48,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;
@@ -182,7 +181,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 +206,112 @@
@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) {
+ 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(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 +1351,7 @@
Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
if (mActivityListenerAdapter == null) {
- mActivityListenerAdapter = createListenerAdapter();
+ mActivityListenerAdapter = new GwpcActivityListener();
}
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
@@ -1306,9 +1367,6 @@
? mParams.getBlockedCrossTaskNavigations()
: mParams.getAllowedCrossTaskNavigations(),
mActivityListenerAdapter,
- this::onActivityBlocked,
- this::onSecureWindowShown,
- this::shouldInterceptIntent,
displayCategories,
showTasksInHostDeviceRecents,
homeComponent);
@@ -1378,28 +1436,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 +1450,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 +1636,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/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/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/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 1a2a196..303828f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -1064,9 +1064,9 @@
private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName,
String historyTag) {
+ long currentTime = mInjector.currentTimeMillis();
mHandler.post(() -> {
if (mFlags.improveWakelockLatency()) {
- long currentTime = mInjector.currentTimeMillis();
if (isEnabled) {
notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags,
workSource, packageName, historyTag, currentTime);
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/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/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d862659..b308680 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3695,9 +3695,6 @@
}
revertTransferOwnershipIfNecessaryLocked();
- if (!Flags.policyEngineMigrationV2Enabled()) {
- updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
- }
}
// Check whether work apps were paused via suspension and unsuspend if necessary.
@@ -15204,7 +15201,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);
@@ -15215,7 +15212,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();
@@ -22440,35 +22437,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)
@@ -22490,24 +22469,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() {
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/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/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/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) {