Merge changes I6ed5a493,If180894e into main
* changes:
Add NotificationRowContentBinderRefactor flag
Re-enable NotificationContentInflaterTest
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 459c286..515ddc8 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -19,16 +19,13 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.perftests.utils.ShellHelper;
-import android.util.Log;
import java.util.ArrayList;
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
public class BenchmarkRunner {
- private static final String TAG = BenchmarkRunner.class.getSimpleName();
+
private static final long COOL_OFF_PERIOD_MS = 1000;
- private static final int CPU_IDLE_TIMEOUT_MS = 60 * 1000;
- private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90;
private static final int NUM_ITERATIONS = 4;
@@ -83,7 +80,7 @@
private void prepareForNextRun() {
SystemClock.sleep(COOL_OFF_PERIOD_MS);
- waitCoolDownPeriod();
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
@@ -105,7 +102,7 @@
* to avoid unnecessary waiting.
*/
public void resumeTiming() {
- waitCoolDownPeriod();
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
resumeTimer();
}
@@ -165,56 +162,4 @@
}
return null;
}
-
- /** Waits for the broadcast queue and the CPU cores to be idle. */
- public void waitCoolDownPeriod() {
- waitForBroadcastIdle();
- waitForCpuIdle();
- }
-
- private void waitForBroadcastIdle() {
- Log.d(TAG, "starting to waitForBroadcastIdle");
- final long startedAt = System.currentTimeMillis();
- ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
- final long elapsed = System.currentTimeMillis() - startedAt;
- Log.d(TAG, "waitForBroadcastIdle is complete in " + elapsed + " ms");
- }
- private void waitForCpuIdle() {
- Log.d(TAG, "starting to waitForCpuIdle");
- final long startedAt = System.currentTimeMillis();
- while (true) {
- final int idleCpuPercentage = getIdleCpuPercentage();
- final long elapsed = System.currentTimeMillis() - startedAt;
- Log.d(TAG, "waitForCpuIdle " + idleCpuPercentage + "% (" + elapsed + "ms elapsed)");
- if (idleCpuPercentage >= CPU_IDLE_THRESHOLD_PERCENTAGE) {
- Log.d(TAG, "waitForCpuIdle is complete in " + elapsed + " ms");
- return;
- }
- if (elapsed >= CPU_IDLE_TIMEOUT_MS) {
- Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in "
- + CPU_IDLE_TIMEOUT_MS + " ms");
- return;
- }
- SystemClock.sleep(1000);
- }
- }
-
- private int getIdleCpuPercentage() {
- String output = ShellHelper.runShellCommand("top -m 1 -n 1");
- String[] tokens = output.split("\\s+");
- float totalCpu = -1;
- float idleCpu = -1;
- for (String token : tokens) {
- if (token.contains("%cpu")) {
- totalCpu = Float.parseFloat(token.split("%")[0]);
- } else if (token.contains("%idle")) {
- idleCpu = Float.parseFloat(token.split("%")[0]);
- }
- }
- if (totalCpu < 0 || idleCpu < 0) {
- Log.e(TAG, "Could not get idle cpu percentage, output=" + output);
- return -1;
- }
- return (int) (100 * idleCpu / totalCpu);
- }
}
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 98ab0c2..762e2af 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -188,6 +188,21 @@
}
}
+ /** Tests creating a new user, with wait times between iterations. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void createUser_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
+ final int userId = createUserNoFlags();
+
+ mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests creating and starting a new user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void createAndStartUser() throws RemoteException {
@@ -224,6 +239,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -238,6 +254,7 @@
mRunner.pauseTiming();
final int userId = createUserNoFlags();
+ waitForBroadcastIdle();
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -292,6 +309,9 @@
preStartUser(userId, numberOfIterationsToSkip);
+ waitForBroadcastIdle();
+ waitCoolDownPeriod();
+
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -333,6 +353,9 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitForBroadcastIdle();
+ waitCoolDownPeriod();
+
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -397,6 +420,7 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -430,6 +454,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -441,7 +466,6 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -455,6 +479,27 @@
}
}
+ /** Tests switching to an uninitialized user with wait times between iterations. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void switchUser_realistic() throws Exception {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = ActivityManager.getCurrentUser();
+ final int userId = createUserNoFlags();
+ waitCoolDownPeriod();
+ Log.d(TAG, "Starting timer");
+ mRunner.resumeTiming();
+
+ switchUser(userId);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ switchUserNoCheck(startUser);
+ removeUser(userId);
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests switching to a previously-started, but no-longer-running, user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser_stopped() throws RemoteException {
@@ -462,7 +507,6 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -492,6 +536,7 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -517,6 +562,7 @@
/* useStaticWallpaper */true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -560,6 +606,7 @@
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -567,6 +614,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -583,6 +631,7 @@
/* useStaticWallpaper */ true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -590,6 +639,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -625,11 +675,13 @@
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void stopUser_realistic() throws RemoteException {
final int userId = createUserNoFlags();
+ waitCoolDownPeriod();
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
runThenWaitForBroadcasts(userId, ()-> {
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -651,7 +703,7 @@
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -674,7 +726,7 @@
final int startUser = ActivityManager.getCurrentUser();
final int userId = createUserNoFlags();
- mRunner.waitCoolDownPeriod();
+ waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -700,7 +752,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -729,7 +781,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- mRunner.waitCoolDownPeriod();
+ waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -775,6 +827,7 @@
Log.d(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -815,6 +868,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -859,6 +913,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
removeUser(userId);
@@ -910,6 +965,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -974,6 +1030,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1014,6 +1071,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1066,6 +1124,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1105,6 +1164,7 @@
runThenWaitForBroadcasts(userId, () -> {
startUserInBackgroundAndWaitForUnlock(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
+ waitCoolDownPeriod();
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -1220,7 +1280,6 @@
* If lack of success should fail the test, use {@link #switchUser(int)} instead.
*/
private boolean switchUserNoCheck(int userId) throws RemoteException {
- mRunner.waitCoolDownPeriod();
final boolean[] success = {true};
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> {
mAm.switchUser(userId);
@@ -1237,7 +1296,7 @@
*/
private void stopUserAfterWaitingForBroadcastIdle(int userId)
throws RemoteException {
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
stopUser(userId);
}
@@ -1379,8 +1438,6 @@
*/
private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
String... actions) {
- mRunner.waitCoolDownPeriod();
-
final String unreceivedAction =
mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);
@@ -1481,4 +1538,28 @@
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
return TextUtils.firstNotEmpty(oldValue, "invalid");
}
+
+ private void waitForBroadcastIdle() {
+ try {
+ ShellHelper.runShellCommandWithTimeout(
+ "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
+ }
+ }
+
+ private void sleep(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ private void waitCoolDownPeriod() {
+ // Heuristic value based on local tests. Stability increased compared to no waiting.
+ final int tenSeconds = 1000 * 10;
+ waitForBroadcastIdle();
+ sleep(tenSeconds);
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7dbf270..76c1ed6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -355,7 +355,7 @@
private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent";
- private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
+ private static final long BINDER_CALLBACK_THROTTLE = 10_100L;
private long mBinderCallbackLast = -1;
/**
@@ -7551,13 +7551,12 @@
@Override
public void onTransactionError(int pid, int code, int flags, int err) {
final long now = SystemClock.uptimeMillis();
- if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE_MS) {
+ if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE) {
Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback.");
return;
}
mBinderCallbackLast = now;
try {
- Log.wtfStack(TAG, "Binder Transaction Error");
mgr.frozenBinderTransactionDetected(pid, code, flags, err);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 44444b5..67752f2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -62,6 +62,7 @@
import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
@@ -17626,6 +17627,17 @@
return onboardingBugreportV2Enabled();
}
+ // TODO(b/308755220): Remove once the build is finalised.
+ /**
+ * Returns true if the flag for consentless bugreports is enabled.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isOnboardingConsentlessBugreportFlagEnabled() {
+ return onboardingConsentlessBugreports();
+ }
+
/**
* Returns the subscription ids of all subscriptions which were downloaded by the calling
* admin.
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 6edae0b..2d78317 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -192,3 +192,10 @@
description: "Removes all custom views"
bug: "342602960"
}
+
+flag {
+ name: "redact_sensitive_content_notifications_on_lockscreen"
+ namespace: "systemui"
+ description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
+ bug: "343631648"
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index e2a131c..5668c54 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -140,6 +140,16 @@
}
}
+flag {
+ name: "fix_avatar_concurrent_file_write"
+ namespace: "multiuser"
+ description: "Fix potential unexpected behavior due to concurrent file writing"
+ bug: "339351031"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 9eabc8d..53771e3 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -963,11 +963,10 @@
* originate from the system, just that we were unable to verify it. This can
* happen for a number of reasons during normal operation.
*
- * @param event The {@link android.view.InputEvent} to check
+ * @param event The {@link android.view.InputEvent} to check.
*
* @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided
- * {@link android.view.InputEvent}
- * {@code null} if the event could not be verified.
+ * {@link android.view.InputEvent}, or {@code null} if the event could not be verified.
*/
@Nullable
public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f357ebf..7933212 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19930,6 +19930,12 @@
public static final String NETWORK_LOCATION_OPT_IN = "network_location_opt_in";
/**
+ * Whether haptics are enabled for Active Unlock on wear.
+ * @hide
+ */
+ public static final String VIBRATE_FOR_ACTIVE_UNLOCK = "wear_vibrate_for_active_unlock";
+
+ /**
* The custom foreground color.
* @hide
*/
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index ba1915c..15b0c13 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2263,6 +2263,7 @@
this(modeId, width, height, refreshRate, vsyncRate, false, alternativeRefreshRates,
supportedHdrTypes);
}
+
/**
* @hide
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index da86e2d..8b9d876 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -213,7 +213,7 @@
/**
* The supported modes that will be exposed externally.
- * Might have different set of modes that supportedModes for VRR displays
+ * Might have different set of modes than supportedModes for VRR displays
*/
public Display.Mode[] appsSupportedModes = Display.Mode.EMPTY_ARRAY;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6db40bf..79a9f2d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -682,7 +682,8 @@
* <li>For a touch screen or touch pad, reports the approximate size of the contact area in
* relation to the maximum detectable size for the device. The value is normalized
* to a range from 0 (smallest detectable size) to 1 (largest detectable size),
- * although it is not a linear scale. This value is of limited use.
+ * although it is not a linear scale. The value of size can be used to
+ * determine fat touch events.
* To obtain calibrated size information, use
* {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR}.
* </ul>
@@ -2795,13 +2796,8 @@
}
/**
- * Returns the current pressure of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The pressure generally
- * ranges from 0 (no pressure at all) to 1 (normal pressure), however
- * values higher than 1 may be generated depending on the calibration of
- * the input device.
+ * Returns the value of {@link #AXIS_PRESSURE} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2812,14 +2808,8 @@
}
/**
- * Returns a scaled value of the approximate size for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * This represents some approximation of the area of the screen being
- * pressed; the actual value in pixels corresponding to the
- * touch is normalized with the device specific range of values
- * and scaled to a value between 0 and 1. The value of size can be used to
- * determine fat touch events.
+ * Returns the value of {@link #AXIS_SIZE} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2830,10 +2820,8 @@
}
/**
- * Returns the length of the major axis of an ellipse that describes the touch
- * area at the point of contact for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
+ * Returns the value of {@link #AXIS_TOUCH_MAJOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2844,10 +2832,8 @@
}
/**
- * Returns the length of the minor axis of an ellipse that describes the touch
- * area at the point of contact for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
+ * Returns the value of {@link #AXIS_TOUCH_MINOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2858,12 +2844,8 @@
}
/**
- * Returns the length of the major axis of an ellipse that describes the size of
- * the approaching tool for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The tool area represents the estimated size of the finger or pen that is
- * touching the device independent of its actual touch area at the point of contact.
+ * Returns the value of {@link #AXIS_TOOL_MAJOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2874,12 +2856,8 @@
}
/**
- * Returns the length of the minor axis of an ellipse that describes the size of
- * the approaching tool for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The tool area represents the estimated size of the finger or pen that is
- * touching the device independent of its actual touch area at the point of contact.
+ * Returns the value of {@link #AXIS_TOOL_MINOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2890,15 +2868,8 @@
}
/**
- * Returns the orientation of the touch area and tool area in radians clockwise from vertical
- * for the given pointer <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * An angle of 0 radians indicates that the major axis of contact is oriented
- * upwards, is perfectly circular or is of unknown orientation. A positive angle
- * indicates that the major axis of contact is oriented to the right. A negative angle
- * indicates that the major axis of contact is oriented to the left.
- * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
- * (finger pointing fully right).
+ * Returns the value of {@link #AXIS_ORIENTATION} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 95c9d7b..5e3f09a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1984,9 +1984,25 @@
public @interface ContentSensitivity {}
/**
- * Automatically determine whether a view displays sensitive content. For example, available
- * autofill hints (or some other signal) can be used to determine if this view
- * displays sensitive content.
+ * Content sensitivity is determined by the framework. The framework uses a heuristic to
+ * determine if this view displays sensitive content.
+ * Autofill hints i.e. {@link #getAutofillHints()} are used in the heuristic
+ * to determine if this view should be considered as a sensitive view.
+ * <p>
+ * {@link #AUTOFILL_HINT_USERNAME},
+ * {@link #AUTOFILL_HINT_PASSWORD},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}
+ * are considered sensitive hints by the framework, and the list may include more hints
+ * in the future.
+ *
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
*
* @see #getContentSensitivity()
*/
@@ -1996,6 +2012,10 @@
/**
* The view displays sensitive content.
*
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+ *
* @see #getContentSensitivity()
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
@@ -10548,9 +10568,13 @@
/**
* Sets content sensitivity mode to determine whether this view displays sensitive content
- * (e.g. username, password etc.). The system may improve user privacy i.e. hide content
+ * (e.g. username, password etc.). The system will improve user privacy i.e. hide content
* drawn by a sensitive view from screen sharing and recording.
*
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+ *
* @param mode {@link #CONTENT_SENSITIVITY_AUTO}, {@link #CONTENT_SENSITIVITY_NOT_SENSITIVE}
* or {@link #CONTENT_SENSITIVITY_SENSITIVE}
*/
@@ -10574,8 +10598,7 @@
* {@link #setContentSensitivity(int)}.
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
- public @ContentSensitivity
- final int getContentSensitivity() {
+ public @ContentSensitivity final int getContentSensitivity() {
return (mPrivateFlags4 & PFLAG4_CONTENT_SENSITIVITY_MASK)
>> PFLAG4_CONTENT_SENSITIVITY_SHIFT;
}
@@ -34061,10 +34084,14 @@
}
private float convertVelocityToFrameRate(float velocityPps) {
+ // From UXR study, premium experience is:
+ // 1500+ dp/s: 120fps
+ // 0 - 1500 dp/s: 80fps
+ // OEMs are likely to modify this to balance battery and user experience for their
+ // specific device.
float density = mAttachInfo.mDensity;
float velocityDps = velocityPps / density;
- // Choose a frame rate in increments of 10fps
- return Math.min(MAX_FRAME_RATE, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
+ return (velocityDps >= 1500f) ? MAX_FRAME_RATE : 80f;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 496e899..fd407d6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2790,11 +2790,27 @@
public void bringChildToFront(View child) {
}
+ // keep in sync with getHostVisibilityReason
int getHostVisibility() {
return mView != null && (mAppVisible || mForceDecorViewVisibility)
? mView.getVisibility() : View.GONE;
}
+ String getHostVisibilityReason() {
+ if (mView == null) {
+ return "mView is null";
+ }
+ if (!mAppVisible && !mForceDecorViewVisibility) {
+ return "!mAppVisible && !mForceDecorViewVisibility";
+ }
+ switch (mView.getVisibility()) {
+ case View.VISIBLE: return "View.VISIBLE";
+ case View.GONE: return "View.GONE";
+ case View.INVISIBLE: return "View.INVISIBLE";
+ default: return "";
+ }
+ }
+
/**
* Add LayoutTransition to the list of transitions to be started in the next traversal.
* This list will be cleared after the transitions on the list are start()'ed. These
@@ -3346,6 +3362,7 @@
int desiredWindowHeight;
final int viewVisibility = getHostVisibility();
+ final String viewVisibilityReason = getHostVisibilityReason();
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
// Also check for possible double visibility update, which will make current
@@ -4220,7 +4237,7 @@
if (!isViewVisible) {
if (mLastTraversalWasVisible) {
- logAndTrace("Not drawing due to not visible");
+ logAndTrace("Not drawing due to not visible. Reason=" + viewVisibilityReason);
}
mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0f54940b..42bf420 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2801,6 +2801,10 @@
* it from appearing in screenshots or from being viewed on non-secure
* displays.
*
+ * <p>See {@link android.view.View#setContentSensitivity(int)}, a window hosting
+ * a sensitive view will be marked as secure during media projection, preventing
+ * it from being viewed on non-secure displays and during screen share.
+ *
* <p>See {@link android.view.Display#FLAG_SECURE} for more details about
* secure surfaces and secure displays.
*/
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index ded142c..d8f1309 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -98,7 +98,7 @@
public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) {
boolean isAlreadyReported =
checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state));
- if (!isAlreadyReported) {
+ if (shouldWriteToStatsLog(isAlreadyReported)) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
changeId, state, mSource);
}
@@ -136,14 +136,16 @@
/**
* Returns whether the next report should be logged to FrameworkStatsLog.
*
- * @param uid affected by the change
- * @param changeId the reported change id
- * @param state of the reported change - enabled/disabled/only logged
+ * @param isAlreadyReported is the change already reported
* @return true if the report should be logged
*/
@VisibleForTesting
- boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
- return !isAlreadyReported(uid, new ChangeReport(changeId, state));
+ boolean shouldWriteToStatsLog(boolean isAlreadyReported) {
+ // We don't log for system server
+ if (mSource == SOURCE_SYSTEM_SERVER) return false;
+
+ // Don't log if already reported
+ return !isAlreadyReported;
}
/**
@@ -160,8 +162,6 @@
boolean isAlreadyReported, int state, boolean isLoggableBySdk) {
// If log all bit is on, always return true.
if (mDebugLogAll) return true;
- // If the change has already been reported, do not write.
- if (isAlreadyReported) return false;
// If the flag is turned off or the TAG's logging is forced to debug level with
// `adb setprop log.tag.CompatChangeReporter=DEBUG`, write to debug since the above checks
@@ -224,6 +224,19 @@
return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report);
}
+ /**
+ * Returns whether the next report should be logged.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @return true if the report should be logged
+ */
+ @VisibleForTesting
+ boolean isAlreadyReported(int uid, long changeId, int state) {
+ return isAlreadyReported(uid, new ChangeReport(changeId, state));
+ }
+
private void markAsReported(int uid, ChangeReport report) {
mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report);
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 3006e20..2068bd7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1411,10 +1411,8 @@
return JNI_TRUE;
}
- if (err == FAILED_TRANSACTION) {
- env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback,
- getpid(), code, flags, err);
- }
+ env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback, getpid(),
+ code, flags, err);
if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 12a42f9..c32fd45 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -31,21 +31,21 @@
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
- public void testStatsLogOnce() {
+ public void testIsAlreadyReportedOnce() {
ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022, otherUid = 1023;
long myChangeId = 500L, otherChangeId = 600L;
int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
// Other reports will be logged.
- assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+ assertFalse(reporter.isAlreadyReported(otherUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, otherChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, otherState));
}
@Test
@@ -55,15 +55,15 @@
long myChangeId = 500L;
int myState = ChangeReporter.STATE_ENABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.resetReportedChanges(myUid);
// Same report will be logged again after reset.
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
}
@Test
@@ -196,4 +196,21 @@
// off.
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myDisabledState, false));
}
+
+ @Test
+ public void testDontLogSystemServer() {
+ ChangeReporter systemServerReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
+
+ // We never log from system server, even if already reported.
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(false));
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(true));
+
+ ChangeReporter unknownSourceReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
+
+ // Otherwise we log if it's not already been reported.
+ assertTrue(unknownSourceReporter.shouldWriteToStatsLog(false));
+ assertFalse(unknownSourceReporter.shouldWriteToStatsLog(true));
+ }
}
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index 025e831..da29828 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -24,6 +24,8 @@
import static android.util.XmlTest.doVerifyWrite;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.os.PersistableBundle;
@@ -41,12 +43,15 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
@RunWith(AndroidJUnit4.class)
public class BinaryXmlTest {
+ private static final int MAX_UNSIGNED_SHORT = 65_535;
+
/**
* Verify that we can write and read large numbers of interned
* {@link String} values.
@@ -170,4 +175,49 @@
}
}
}
+
+ @Test
+ public void testAttributeBytes_BinaryDataOverflow() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1];
+ assertThrows(IOException.class,
+ () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex",
+ testBytes));
+
+ assertThrows(IOException.class,
+ () -> out.attributeBytesBase64(/* namespace */ null, /* name */
+ "attributeBytesBase64", testBytes));
+ }
+
+ @Test
+ public void testAttributeBytesHex_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesHex fails with exception: " + e.toString());
+ }
+ }
+
+ @Test
+ public void testAttributeBytesBase64_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64",
+ testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesBase64 fails with exception: " + e.toString());
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 07446e7..abe9c8e 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -164,7 +164,7 @@
mActivityRule.runOnUiThread(() -> {
mMovingView.setFrameContentVelocity(1f);
mMovingView.invalidate();
- runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+ runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
});
waitForAfterDraw();
}
@@ -190,7 +190,7 @@
frameLayout.setFrameContentVelocity(1f);
mMovingView.offsetTopAndBottom(100);
frameLayout.invalidate();
- runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+ runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
});
waitForAfterDraw();
}
@@ -435,7 +435,7 @@
runAfterDraw(() -> {
assertEquals(FRAME_RATE_CATEGORY_LOW,
mViewRoot.getLastPreferredFrameRateCategory());
- assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(80f, mViewRoot.getLastPreferredFrameRate());
});
});
waitForAfterDraw();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 774b212..23dc96c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -245,11 +245,9 @@
isReversedLayout,
parentInfo.getDisplayId(),
isDraggableExpandType,
- getContainerBackgroundColor(
- primaryContainer, DEFAULT_PRIMARY_VEIL_COLOR),
- getContainerBackgroundColor(
- secondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR)
- ));
+ primaryContainer,
+ secondaryContainer)
+ );
}
}
@@ -965,8 +963,10 @@
private final int mDisplayId;
private final boolean mIsReversedLayout;
private final boolean mIsDraggableExpandType;
- private final Color mPrimaryVeilColor;
- private final Color mSecondaryVeilColor;
+ @NonNull
+ private final TaskFragmentContainer mPrimaryContainer;
+ @NonNull
+ private final TaskFragmentContainer mSecondaryContainer;
private final int mDividerWidthPx;
@VisibleForTesting
@@ -979,8 +979,8 @@
boolean isReversedLayout,
int displayId,
boolean isDraggableExpandType,
- @NonNull Color primaryVeilColor,
- @NonNull Color secondaryVeilColor) {
+ @NonNull TaskFragmentContainer primaryContainer,
+ @NonNull TaskFragmentContainer secondaryContainer) {
mConfiguration = configuration;
mDividerAttributes = dividerAttributes;
mDecorSurface = decorSurface;
@@ -989,8 +989,8 @@
mIsReversedLayout = isReversedLayout;
mDisplayId = displayId;
mIsDraggableExpandType = isDraggableExpandType;
- mPrimaryVeilColor = primaryVeilColor;
- mSecondaryVeilColor = secondaryVeilColor;
+ mPrimaryContainer = primaryContainer;
+ mSecondaryContainer = secondaryContainer;
mDividerWidthPx = getDividerWidthPx(dividerAttributes);
}
@@ -1014,8 +1014,8 @@
&& a.mDisplayId == b.mDisplayId
&& a.mIsReversedLayout == b.mIsReversedLayout
&& a.mIsDraggableExpandType == b.mIsDraggableExpandType
- && a.mPrimaryVeilColor.equals(b.mPrimaryVeilColor)
- && a.mSecondaryVeilColor.equals(b.mSecondaryVeilColor);
+ && a.mPrimaryContainer == b.mPrimaryContainer
+ && a.mSecondaryContainer == b.mSecondaryContainer;
}
private static boolean areSameSurfaces(
@@ -1328,8 +1328,12 @@
}
private void showVeils(@NonNull SurfaceControl.Transaction t) {
- t.setColor(mPrimaryVeil, colorToFloatArray(mProperties.mPrimaryVeilColor))
- .setColor(mSecondaryVeil, colorToFloatArray(mProperties.mSecondaryVeilColor))
+ final Color primaryVeilColor = getContainerBackgroundColor(
+ mProperties.mPrimaryContainer, DEFAULT_PRIMARY_VEIL_COLOR);
+ final Color secondaryVeilColor = getContainerBackgroundColor(
+ mProperties.mSecondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR);
+ t.setColor(mPrimaryVeil, colorToFloatArray(primaryVeilColor))
+ .setColor(mSecondaryVeil, colorToFloatArray(secondaryVeilColor))
.setLayer(mDividerSurface, DIVIDER_LAYER)
.setLayer(mPrimaryVeil, VEIL_LAYER)
.setLayer(mSecondaryVeil, VEIL_LAYER)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index 4515187..3f67607 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -166,8 +166,8 @@
false /* isReversedLayout */,
Display.DEFAULT_DISPLAY,
false /* isDraggableExpandType */,
- Color.valueOf(Color.BLACK), /* primaryVeilColor */
- Color.valueOf(Color.GRAY) /* secondaryVeilColor */
+ mockPrimaryContainer,
+ mockSecondaryContainer
);
mDividerPresenter = new DividerPresenter(
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 08e695b..15f8c32 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -1,3 +1,5 @@
+# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+
package: "com.android.wm.shell"
container: "system"
@@ -99,3 +101,13 @@
description: "Enable UI affordances to put other content into a bubble"
bug: "342245211"
}
+
+flag {
+ name: "only_reuse_bubbled_task_when_launched_from_bubble"
+ namespace: "multitasking"
+ description: "Allow reusing bubbled tasks for new activities only when launching from bubbles"
+ bug: "328229865"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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 12d1927..ace2c13 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
@@ -26,7 +26,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
@@ -35,6 +35,8 @@
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.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -63,6 +65,9 @@
private lateinit var controller: BubbleExpandedViewPinController
private lateinit var testListener: TestLocationChangeListener
+ private val dropTargetView: View?
+ get() = container.findViewById(R.id.bubble_bar_drop_target)
+
private val pointOnLeft = PointF(100f, 100f)
private val pointOnRight = PointF(1900f, 500f)
@@ -92,13 +97,14 @@
@After
fun tearDown() {
- runOnMainSync { controller.onDragEnd() }
+ getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
}
+ /** Dragging on same side should not show drop target or trigger location changes */
@Test
- fun drag_stayOnSameSide() {
- runOnMainSync {
+ fun drag_stayOnRightSide() {
+ getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragEnd()
@@ -106,71 +112,124 @@
waitForAnimateIn()
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
- assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
+ assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
+ /** Dragging on same side should not show drop target or trigger location changes */
@Test
- fun drag_toLeft() {
- // Drag to left, but don't finish
- runOnMainSync {
+ fun drag_stayOnLeftSide() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onDragEnd()
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).isEmpty()
+ assertThat(testListener.locationReleases).containsExactly(LEFT)
+ }
+
+ /** Drag crosses to the other side. Show drop target and trigger a location change. */
+ @Test
+ fun drag_rightToLeft() {
+ getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
-
- val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = true)
- assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
- assertThat(dropTargetView!!.layoutParams.height)
- .isEqualTo(expectedDropTargetBounds.height())
-
- assertThat(testListener.locationChanges).containsExactly(BubbleBarLocation.LEFT)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+ assertThat(testListener.locationChanges).containsExactly(LEFT)
assertThat(testListener.locationReleases).isEmpty()
-
- // Finish the drag
- runOnMainSync { controller.onDragEnd() }
- assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.LEFT)
}
+ /** Drag crosses to the other side. Show drop target and trigger a location change. */
@Test
- fun drag_toLeftAndBackToRight() {
- // Drag to left
- runOnMainSync {
- controller.onDragStart(initialLocationOnLeft = false)
+ fun drag_leftToRight() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
}
waitForAnimateIn()
+
+ assertThat(dropTargetView).isNotNull()
+ assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+ assertThat(testListener.locationChanges).containsExactly(RIGHT)
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drop target does not initially show on the side that the drag starts. Check that it shows up
+ * after the dragging the view to other side and back to the initial side.
+ */
+ @Test
+ fun drag_rightToLeftToRight() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+
+ getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
+ waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
- // Drag to right
- runOnMainSync { controller.onDragUpdate(pointOnRight.x, pointOnRight.y) }
- // We have to wait for existing drop target to animate out and new to animate in
+ getInstrumentation().runOnMainSync {
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
waitForAnimateOut()
waitForAnimateIn()
-
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
-
- val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = false)
- assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
- assertThat(dropTargetView!!.layoutParams.height)
- .isEqualTo(expectedDropTargetBounds.height())
-
- assertThat(testListener.locationChanges)
- .containsExactly(BubbleBarLocation.LEFT, BubbleBarLocation.RIGHT)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+ assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
assertThat(testListener.locationReleases).isEmpty()
-
- // Release the view
- runOnMainSync { controller.onDragEnd() }
- assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
}
+ /**
+ * Drop target does not initially show on the side that the drag starts. Check that it shows up
+ * after the dragging the view to other side and back to the initial side.
+ */
@Test
- fun drag_toLeftInExclusionRect() {
- runOnMainSync {
+ fun drag_leftToRightToLeft() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+
+ getInstrumentation().runOnMainSync {
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNotNull()
+
+ getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
+ waitForAnimateOut()
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNotNull()
+ assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+ assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drag from right to left, but stay in exclusion rect around the dismiss view. Drop target
+ * should not show and location change should not trigger.
+ */
+ @Test
+ fun drag_rightToLeft_inExclusionRect() {
+ getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
// Exclusion rect is around the bottom center area of the screen
controller.onDragUpdate(SCREEN_WIDTH / 2f - 50, SCREEN_HEIGHT - 100f)
}
@@ -178,85 +237,212 @@
assertThat(dropTargetView).isNull()
assertThat(testListener.locationChanges).isEmpty()
assertThat(testListener.locationReleases).isEmpty()
-
- runOnMainSync { controller.onDragEnd() }
- assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
}
+ /**
+ * Drag from left to right, but stay in exclusion rect around the dismiss view. Drop target
+ * should not show and location change should not trigger.
+ */
@Test
- fun toggleSetDropTargetHidden_dropTargetExists() {
- runOnMainSync {
+ fun drag_leftToRight_inExclusionRect() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ // Exclusion rect is around the bottom center area of the screen
+ controller.onDragUpdate(SCREEN_WIDTH / 2f + 50, SCREEN_HEIGHT - 100f)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).isEmpty()
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drag to dismiss target and back to the same side should not cause the drop target to show.
+ */
+ @Test
+ fun drag_rightToDismissToRight() {
+ getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ controller.onStuckToDismissTarget()
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).isEmpty()
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drag to dismiss target and back to the same side should not cause the drop target to show.
+ */
+ @Test
+ fun drag_leftToDismissToLeft() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onStuckToDismissTarget()
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
+ assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).isEmpty()
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+ /** Drag to dismiss target and other side should show drop target on the other side. */
+ @Test
+ fun drag_rightToDismissToLeft() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ controller.onStuckToDismissTarget()
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNotNull()
+ assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+
+ assertThat(testListener.locationChanges).containsExactly(LEFT)
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /** Drag to dismiss target and other side should show drop target on the other side. */
+ @Test
+ fun drag_leftToDismissToRight() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onStuckToDismissTarget()
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNotNull()
+ assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+ assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+
+ assertThat(testListener.locationChanges).containsExactly(RIGHT)
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drag to dismiss should trigger a location change to the initial location, if the current
+ * location is different. And hide the drop target.
+ */
+ @Test
+ fun drag_rightToLeftToDismiss() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ }
+ waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
- runOnMainSync { controller.setDropTargetHidden(true) }
+ getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
- assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(0f)
- runOnMainSync { controller.setDropTargetHidden(false) }
+ assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
+ assertThat(testListener.locationReleases).isEmpty()
+ }
+
+ /**
+ * Drag to dismiss should trigger a location change to the initial location, if the current
+ * location is different. And hide the drop target.
+ */
+ @Test
+ fun drag_leftToRightToDismiss() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
assertThat(dropTargetView!!.alpha).isEqualTo(1f)
- }
-
- @Test
- fun toggleSetDropTargetHidden_noDropTarget() {
- runOnMainSync { controller.setDropTargetHidden(true) }
+ getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
waitForAnimateOut()
- assertThat(dropTargetView).isNull()
-
- runOnMainSync { controller.setDropTargetHidden(false) }
- waitForAnimateIn()
- assertThat(dropTargetView).isNull()
+ assertThat(dropTargetView!!.alpha).isEqualTo(0f)
+ assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
+ assertThat(testListener.locationReleases).isEmpty()
}
+ /** Finishing drag should remove drop target and send location update. */
@Test
- fun onDragEnd_dropTargetExists() {
- runOnMainSync {
+ fun drag_rightToLeftRelease() {
+ getInstrumentation().runOnMainSync {
controller.onDragStart(initialLocationOnLeft = false)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
}
waitForAnimateIn()
assertThat(dropTargetView).isNotNull()
- runOnMainSync { controller.onDragEnd() }
+ getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).containsExactly(LEFT)
+ assertThat(testListener.locationReleases).containsExactly(LEFT)
}
+ /** Finishing drag should remove drop target and send location update. */
@Test
- fun onDragEnd_noDropTarget() {
- runOnMainSync { controller.onDragEnd() }
+ fun drag_leftToRightRelease() {
+ getInstrumentation().runOnMainSync {
+ controller.onDragStart(initialLocationOnLeft = true)
+ controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+ controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+ }
+ waitForAnimateIn()
+ assertThat(dropTargetView).isNotNull()
+
+ getInstrumentation().runOnMainSync { controller.onDragEnd() }
waitForAnimateOut()
assertThat(dropTargetView).isNull()
+ assertThat(testListener.locationChanges).containsExactly(RIGHT)
+ assertThat(testListener.locationReleases).containsExactly(RIGHT)
}
- private val dropTargetView: View?
- get() = container.findViewById(R.id.bubble_bar_drop_target)
-
- private fun getExpectedDropTargetBounds(onLeft: Boolean): Rect =
+ private fun getExpectedDropTargetBoundsOnLeft(): Rect =
Rect().also {
- positioner.getBubbleBarExpandedViewBounds(onLeft, false /* isOveflowExpanded */, it)
+ positioner.getBubbleBarExpandedViewBounds(
+ true /* onLeft */,
+ false /* isOverflowExpanded */,
+ it
+ )
}
- private fun runOnMainSync(runnable: Runnable) {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable)
- }
+ private fun getExpectedDropTargetBoundsOnRight(): Rect =
+ Rect().also {
+ positioner.getBubbleBarExpandedViewBounds(
+ false /* onLeft */,
+ false /* isOverflowExpanded */,
+ it
+ )
+ }
private fun waitForAnimateIn() {
// Advance animator for on-device test
- runOnMainSync { animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION) }
+ getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION)
+ }
}
private fun waitForAnimateOut() {
// Advance animator for on-device test
- runOnMainSync { animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION) }
+ getInstrumentation().runOnMainSync {
+ animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION)
+ }
+ }
+
+ private fun View.bounds(): Rect {
+ return Rect(0, 0, layoutParams.width, layoutParams.height).also { rect ->
+ rect.offsetTo(x.toInt(), y.toInt())
+ }
}
internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index ee740fb..9114c7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -27,6 +27,7 @@
import android.graphics.Rect
import android.graphics.RectF
import android.os.RemoteException
+import android.util.TimeUtils
import android.view.Choreographer
import android.view.Display
import android.view.IRemoteAnimationFinishedCallback
@@ -109,6 +110,7 @@
private val postCommitFlingSpring = SpringForce(SPRING_SCALE)
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ protected var gestureProgress = 0f
/** Background color to be used during the animation, also see [getBackgroundColor] */
protected var customizedBackgroundColor = 0
@@ -212,6 +214,7 @@
private fun onGestureProgress(backEvent: BackEvent) {
val progress = gestureInterpolator.getInterpolation(backEvent.progress)
+ gestureProgress = progress
currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
val yOffset = getYOffset(currentClosingRect, backEvent.touchY)
currentClosingRect.offset(0f, yOffset)
@@ -257,12 +260,16 @@
}
// kick off spring animation with the current velocity from the pre-commit phase, this
- // affects the scaling of the closing activity during post-commit
+ // affects the scaling of the closing and/or opening activity during post-commit
+ val startVelocity =
+ if (gestureProgress < 0.1f) -DEFAULT_FLING_VELOCITY else -velocity * SPRING_SCALE
val flingAnimation = SpringAnimation(postCommitFlingScale, SPRING_SCALE)
- .setStartVelocity(min(0f, -velocity * SPRING_SCALE))
+ .setStartVelocity(startVelocity.coerceIn(-MAX_FLING_VELOCITY, 0f))
.setStartValue(SPRING_SCALE)
.setSpring(postCommitFlingSpring)
flingAnimation.start()
+ // do an animation-frame immediately to prevent idle frame
+ flingAnimation.doAnimationFrame(choreographer.lastFrameTimeNanos / TimeUtils.NANOS_PER_MS)
val valueAnimator =
ValueAnimator.ofFloat(1f, 0f).setDuration(getPostCommitAnimationDuration())
@@ -292,6 +299,7 @@
enteringTarget?.let {
if (it.leash != null && it.leash.isValid) {
transaction.setCornerRadius(it.leash, 0f)
+ if (!triggerBack) transaction.setAlpha(it.leash, 0f)
it.leash.release()
}
enteringTarget = null
@@ -315,19 +323,22 @@
isLetterboxed = false
enteringHasSameLetterbox = false
lastPostCommitFlingScale = SPRING_SCALE
+ gestureProgress = 0f
}
protected fun applyTransform(
leash: SurfaceControl?,
rect: RectF,
alpha: Float,
- baseTransformation: Transformation? = null
+ baseTransformation: Transformation? = null,
+ flingMode: FlingMode = FlingMode.NO_FLING
) {
if (leash == null || !leash.isValid) return
tempRectF.set(rect)
- if (leash == closingTarget?.leash) {
- lastPostCommitFlingScale = (postCommitFlingScale.value / SPRING_SCALE).coerceIn(
- minimumValue = MAX_FLING_SCALE, maximumValue = lastPostCommitFlingScale
+ if (flingMode != FlingMode.NO_FLING) {
+ lastPostCommitFlingScale = min(
+ postCommitFlingScale.value / SPRING_SCALE,
+ if (flingMode == FlingMode.FLING_BOUNCE) 1f else lastPostCommitFlingScale
)
// apply an additional scale to the closing target to account for fling velocity
tempRectF.scaleCentered(lastPostCommitFlingScale)
@@ -529,14 +540,30 @@
private const val MAX_SCRIM_ALPHA_DARK = 0.8f
private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
private const val SPRING_SCALE = 100f
- private const val MAX_FLING_SCALE = 0.6f
+ private const val MAX_FLING_VELOCITY = 1000f
+ private const val DEFAULT_FLING_VELOCITY = 120f
+ }
+
+ enum class FlingMode {
+ NO_FLING,
+
+ /**
+ * This is used for the closing target in custom cross-activity back animations. When the
+ * back gesture is flung, the closing target shrinks a bit further with a spring motion.
+ */
+ FLING_SHRINK,
+
+ /**
+ * This is used for the closing and opening target in the default cross-activity back
+ * animation. When the back gesture is flung, the closing and opening targets shrink a
+ * bit further and then bounce back with a spring motion.
+ */
+ FLING_BOUNCE
}
}
// The target will loose focus when alpha == 0, so keep a minimum value for it.
-private fun keepMinimumAlpha(transAlpha: Float): Float {
- return max(transAlpha.toDouble(), 0.005).toFloat()
-}
+private fun keepMinimumAlpha(transAlpha: Float) = max(transAlpha, 0.005f)
private fun isDarkMode(context: Context): Boolean {
return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index c4aafea..c738ce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -57,7 +57,6 @@
private var enterAnimation: Animation? = null
private var closeAnimation: Animation? = null
private val transformation = Transformation()
- private var gestureProgress = 0f
override val allowEnteringYShift = false
@@ -105,7 +104,6 @@
}
override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
- gestureProgress = progress
transformation.clear()
enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
return transformation
@@ -134,7 +132,13 @@
if (closingTarget == null || enteringTarget == null) return
val closingProgress = closeAnimation!!.getPostCommitProgress(linearProgress)
- applyTransform(closingTarget!!.leash, currentClosingRect, closingProgress, closeAnimation!!)
+ applyTransform(
+ closingTarget!!.leash,
+ currentClosingRect,
+ closingProgress,
+ closeAnimation!!,
+ FlingMode.FLING_SHRINK
+ )
val enteringProgress = MathUtils.lerp(
gestureProgress * PRE_COMMIT_MAX_PROGRESS,
1f,
@@ -144,7 +148,8 @@
enteringTarget!!.leash,
currentEnteringRect,
enteringProgress,
- enterAnimation!!
+ enterAnimation!!,
+ FlingMode.NO_FLING
)
applyTransaction()
}
@@ -153,11 +158,12 @@
leash: SurfaceControl,
rect: RectF,
progress: Float,
- animation: Animation
+ animation: Animation,
+ flingMode: FlingMode
) {
transformation.clear()
animation.getTransformationAt(progress, transformation)
- applyTransform(leash, rect, transformation.alpha, transformation)
+ applyTransform(leash, rect, transformation.alpha, transformation, flingMode)
}
override fun finishAnimation() {
@@ -166,7 +172,6 @@
enterAnimation?.reset()
enterAnimation = null
transformation.clear()
- gestureProgress = 0f
super.finishAnimation()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index 44752fe..3b5eb36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -43,7 +43,7 @@
Choreographer.getInstance()
) {
- private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+ private val postCommitInterpolator = Interpolators.EMPHASIZED
private val enteringStartOffset =
context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
override val allowEnteringYShift = true
@@ -87,17 +87,27 @@
override fun onPostCommitProgress(linearProgress: Float) {
super.onPostCommitProgress(linearProgress)
- val closingAlpha = max(1f - linearProgress * 2, 0f)
+ val closingAlpha = max(1f - linearProgress * 5, 0f)
val progress = postCommitInterpolator.getInterpolation(linearProgress)
currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
- applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+ applyTransform(
+ closingTarget?.leash,
+ currentClosingRect,
+ closingAlpha,
+ flingMode = FlingMode.FLING_BOUNCE
+ )
currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
- applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+ applyTransform(
+ enteringTarget?.leash,
+ currentEnteringRect,
+ 1f,
+ flingMode = FlingMode.FLING_BOUNCE
+ )
applyTransaction()
}
companion object {
- private const val POST_COMMIT_DURATION = 300L
+ private const val POST_COMMIT_DURATION = 450L
}
}
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 a51ac63..fa1091c 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
@@ -150,7 +150,7 @@
draggedObject: MagnetizedObject<*>
) {
isStuckToDismiss = true
- pinController.setDropTargetHidden(true)
+ pinController.onStuckToDismissTarget()
}
override fun onUnstuckFromTarget(
@@ -162,7 +162,6 @@
) {
isStuckToDismiss = false
animationHelper.animateUnstuckFromDismissView(target)
- pinController.setDropTargetHidden(false)
}
override fun onReleasedInTarget(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
index e514f9d..eec2468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
@@ -38,8 +38,10 @@
*/
abstract class BaseBubblePinController(private val screenSizeProvider: () -> Point) {
+ private var initialLocationOnLeft = false
private var onLeft = false
private var dismissZone: RectF? = null
+ private var stuckToDismissTarget = false
private var screenCenterX = 0
private var listener: LocationChangeListener? = null
private var dropTargetAnimator: ObjectAnimator? = null
@@ -50,6 +52,7 @@
* @param initialLocationOnLeft side of the screen where bubble bar is pinned to
*/
fun onDragStart(initialLocationOnLeft: Boolean) {
+ this.initialLocationOnLeft = initialLocationOnLeft
onLeft = initialLocationOnLeft
screenCenterX = screenSizeProvider.invoke().x / 2
dismissZone = getExclusionRect()
@@ -59,22 +62,33 @@
fun onDragUpdate(x: Float, y: Float) {
if (dismissZone?.contains(x, y) == true) return
- if (onLeft && x > screenCenterX) {
- onLeft = false
- onLocationChange(RIGHT)
- } else if (!onLeft && x < screenCenterX) {
- onLeft = true
- onLocationChange(LEFT)
+ val wasOnLeft = onLeft
+ onLeft = x < screenCenterX
+ if (wasOnLeft != onLeft) {
+ onLocationChange(if (onLeft) LEFT else RIGHT)
+ } else if (stuckToDismissTarget) {
+ // Moved out of the dismiss view back to initial side, if we have a drop target, show it
+ getDropTargetView()?.apply { animateIn() }
}
+ // Make sure this gets cleared
+ stuckToDismissTarget = false
}
- /** Temporarily hide the drop target view */
- fun setDropTargetHidden(hidden: Boolean) {
- val targetView = getDropTargetView() ?: return
- if (hidden) {
- targetView.animateOut()
- } else {
- targetView.animateIn()
+ /** Signal the controller that view has been dragged to dismiss view. */
+ fun onStuckToDismissTarget() {
+ stuckToDismissTarget = true
+ // Notify that location may be reset
+ val shouldResetLocation = onLeft != initialLocationOnLeft
+ if (shouldResetLocation) {
+ onLeft = initialLocationOnLeft
+ listener?.onChange(if (onLeft) LEFT else RIGHT)
+ }
+ getDropTargetView()?.apply {
+ animateOut {
+ if (shouldResetLocation) {
+ updateLocation(if (onLeft) LEFT else RIGHT)
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index c79eef7e..cd478e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -143,6 +143,10 @@
}
public static boolean handles(TransitionInfo info) {
+ // There is no animation for screen-wake unless we are immediately unlocking.
+ if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
+ return false;
+ }
return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0;
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5ce990f..7b7ccf5 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -48,7 +48,6 @@
"libgui",
"libui",
"libinput",
- "libnativewindow",
],
header_libs: [
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index cbef68e..5b00fca 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -162,6 +162,16 @@
};
class PointerControllerTest : public Test {
+private:
+ void loopThread();
+
+ std::atomic<bool> mRunning = true;
+ class MyLooper : public Looper {
+ public:
+ MyLooper() : Looper(false) {}
+ ~MyLooper() = default;
+ };
+
protected:
PointerControllerTest();
~PointerControllerTest();
@@ -173,26 +183,16 @@
std::unique_ptr<MockSpriteController> mSpriteController;
std::shared_ptr<PointerController> mPointerController;
sp<android::gui::WindowInfosListener> mRegisteredListener;
+ sp<MyLooper> mLooper;
private:
- void loopThread();
-
- std::atomic<bool> mRunning = true;
- class MyLooper : public Looper {
- public:
- MyLooper() : Looper(false) {}
- ~MyLooper() = default;
- };
std::thread mThread;
-
-protected:
- sp<MyLooper> mLooper;
};
PointerControllerTest::PointerControllerTest()
: mPointerSprite(new NiceMock<MockSprite>),
- mThread(&PointerControllerTest::loopThread, this),
- mLooper(new MyLooper) {
+ mLooper(new MyLooper),
+ mThread(&PointerControllerTest::loopThread, this) {
mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
mPolicy = new MockPointerControllerPolicyInterface();
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
index 4c76fb0..5dcc84c 100644
--- a/nfc/java/android/nfc/cardemulation/PollingFrame.java
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -16,7 +16,6 @@
package android.nfc.cardemulation;
-import android.annotation.DurationMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -33,13 +32,13 @@
/**
* Polling Frames represent data about individual frames of an NFC polling loop. These frames will
- * be deliverd to subclasses of {@link HostApduService} that have registered filters with
- * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
- * given frame in a loop and will be delivered through calls to
+ * be delivered to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String, boolean)} that
+ * match a given frame in a loop and will be delivered through calls to
* {@link HostApduService#processPollingFrames(List)}.
*/
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-public final class PollingFrame implements Parcelable{
+public final class PollingFrame implements Parcelable {
/**
* @hide
@@ -146,7 +145,6 @@
private final int mType;
private final byte[] mData;
private final int mGain;
- @DurationMillisLong
private final long mTimestamp;
private boolean mTriggeredAutoTransact;
@@ -179,18 +177,18 @@
* @param type the type of the frame
* @param data a byte array of the data contained in the frame
* @param gain the vendor-specific gain of the field
- * @param timestampMillis the timestamp in millisecones
+ * @param timestampMicros the timestamp in microseconds
* @param triggeredAutoTransact whether or not this frame triggered the device to start a
* transaction automatically
*
* @hide
*/
public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
- int gain, @DurationMillisLong long timestampMillis, boolean triggeredAutoTransact) {
+ int gain, long timestampMicros, boolean triggeredAutoTransact) {
mType = type;
mData = data == null ? new byte[0] : data;
mGain = gain;
- mTimestamp = timestampMillis;
+ mTimestamp = timestampMicros;
mTriggeredAutoTransact = triggeredAutoTransact;
}
@@ -198,11 +196,11 @@
* Returns the type of frame for this polling loop frame.
* The possible return values are:
* <ul>
- * <li>{@link POLLING_LOOP_TYPE_ON}</li>
- * <li>{@link POLLING_LOOP_TYPE_OFF}</li>
- * <li>{@link POLLING_LOOP_TYPE_A}</li>
- * <li>{@link POLLING_LOOP_TYPE_B}</li>
- * <li>{@link POLLING_LOOP_TYPE_F}</li>
+ * <li>{@link #POLLING_LOOP_TYPE_ON}</li>
+ * <li>{@link #POLLING_LOOP_TYPE_OFF}</li>
+ * <li>{@link #POLLING_LOOP_TYPE_A}</li>
+ * <li>{@link #POLLING_LOOP_TYPE_B}</li>
+ * <li>{@link #POLLING_LOOP_TYPE_F}</li>
* </ul>
*/
public @PollingFrameType int getType() {
@@ -226,12 +224,12 @@
}
/**
- * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
- * timestamps are relative and not absolute and should only be used for comparing the timing of
- * frames relative to each other.
- * @return the timestamp in milliseconds
+ * Returns the timestamp of when the polling loop frame was observed, in microseconds. These
+ * timestamps are relative and should only be used for comparing the timing of frames relative
+ * to each other.
+ * @return the timestamp in microseconds
*/
- public @DurationMillisLong long getTimestamp() {
+ public long getTimestamp() {
return mTimestamp;
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index 9c8ec3b..3022fa0 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -27,6 +27,7 @@
import android.credentials.selection.Entry
import android.credentials.selection.GetCredentialProviderData
import android.graphics.drawable.Drawable
+import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import androidx.activity.result.IntentSenderRequest
@@ -227,26 +228,31 @@
* and get flows utilize slice params; includes the final '.' before the name of the type (e.g.
* androidx.credentials.provider.credentialEntry.SLICE_HINT_ALLOWED_AUTHENTICATORS must have
* 'hintPrefix' up to "androidx.credentials.provider.credentialEntry.")
- * // TODO(b/326243754) : Presently, due to dependencies, the opId bit is parsed but is never
- * // expected to be used. When it is added, it should be lightly validated.
*/
fun retrieveEntryBiometricRequest(
entry: Entry,
- hintPrefix: String,
+ hintPrefix: String
): BiometricRequestInfo? {
- // TODO(b/326243754) : When available, use the official jetpack structured type
- val allowedAuthenticators: Int? = entry.slice.items.firstOrNull {
- it.hasHint(hintPrefix + "SLICE_HINT_ALLOWED_AUTHENTICATORS")
- }?.int
+ // TODO(b/326243754) : When available, use the official jetpack structured typLo
+ val biometricPromptDataBundleKey = "SLICE_HINT_BIOMETRIC_PROMPT_DATA"
+ val biometricPromptDataBundle: Bundle = entry.slice.items.firstOrNull {
+ it.hasHint(hintPrefix + biometricPromptDataBundleKey)
+ }?.bundle ?: return null
+
+ val allowedAuthConstantKey = "androidx.credentials.provider.BUNDLE_HINT_ALLOWED_AUTHENTICATORS"
+ val cryptoOpIdKey = "androidx.credentials.provider.BUNDLE_HINT_CRYPTO_OP_ID"
+
+ if (!biometricPromptDataBundle.containsKey(allowedAuthConstantKey)) {
+ return null
+ }
+
+ val allowedAuthenticators: Int = biometricPromptDataBundle.getInt(allowedAuthConstantKey)
// This is optional and does not affect validating the biometric flow in any case
- val opId: Int? = entry.slice.items.firstOrNull {
- it.hasHint(hintPrefix + "SLICE_HINT_CRYPTO_OP_ID")
- }?.int
- if (allowedAuthenticators != null) {
- return BiometricRequestInfo(opId = opId, allowedAuthenticators = allowedAuthenticators)
- }
- return null
+ val opId: Long? = if (biometricPromptDataBundle.containsKey(cryptoOpIdKey))
+ biometricPromptDataBundle.getLong(cryptoOpIdKey) else null
+
+ return BiometricRequestInfo(opId = opId, allowedAuthenticators = allowedAuthenticators)
}
val Slice.credentialEntry: CredentialEntry?
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
index 486cfe7..fe4bead 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
@@ -23,6 +23,6 @@
* null.
*/
data class BiometricRequestInfo(
- val opId: Int? = null,
+ val opId: Long? = null,
val allowedAuthenticators: Int
)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 7bc25ed..894d5ef 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -135,16 +135,18 @@
Log.w(Constants.LOG_TAG, "Unexpected biometric result exists when " +
"autoSelect is preferred.")
}
- // TODO(b/333445754) : Decide whether to propagate info on prompt launch
+ // TODO(b/333445754) : Change the fm option to false in qpr after discussion
if (biometricState.biometricResult != null) {
entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_RESULT,
biometricState.biometricResult.biometricAuthenticationResult
.authenticationType)
+ entryIntent?.putExtra(Constants.BIOMETRIC_FRAMEWORK_OPTION, true)
} else if (biometricState.biometricError != null){
entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_CODE,
biometricState.biometricError.errorCode)
entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_MESSAGE,
biometricState.biometricError.errorMessage)
+ entryIntent?.putExtra(Constants.BIOMETRIC_FRAMEWORK_OPTION, true)
}
}
val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index 373b3e8..c35721c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -230,7 +230,7 @@
val cryptoOpId = getCryptoOpId(biometricDisplayInfo)
if (cryptoOpId != null) {
biometricPrompt.authenticate(
- BiometricPrompt.CryptoObject(cryptoOpId.toLong()),
+ BiometricPrompt.CryptoObject(cryptoOpId),
cancellationSignal, executor, callback)
} else {
biometricPrompt.authenticate(cancellationSignal, executor, callback)
@@ -243,7 +243,7 @@
return true
}
-private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
+private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Long? {
return biometricDisplayInfo.biometricRequestInfo.opId
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 3c80113..cb089ad 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -22,9 +22,12 @@
const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
"androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
- // TODO(b/333445772) : Qualify error codes fully for propagation
- const val BIOMETRIC_AUTH_RESULT = "BIOMETRIC_AUTH_RESULT"
- const val BIOMETRIC_AUTH_ERROR_CODE = "BIOMETRIC_AUTH_ERROR_CODE"
- const val BIOMETRIC_AUTH_ERROR_MESSAGE = "BIOMETRIC_AUTH_ERROR_MESSAGE"
+ const val BIOMETRIC_AUTH_RESULT = "androidx.credentials.provider.BIOMETRIC_AUTH_RESULT"
+ const val BIOMETRIC_AUTH_ERROR_CODE =
+ "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_CODE"
+ const val BIOMETRIC_AUTH_ERROR_MESSAGE =
+ "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_MESSAGE"
+ const val BIOMETRIC_FRAMEWORK_OPTION =
+ "androidx.credentials.provider.BIOMETRIC_FRAMEWORK_OPTION"
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
index 39f2fce..dac25fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
@@ -17,7 +17,6 @@
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
enum class CreateCredentialEvent(private val id: Int) : UiEventLogger.UiEventEnum {
@@ -56,7 +55,7 @@
CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328),
@UiEvent(doc = "The single tap biometric flow is launched.")
- CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED(RESERVE_NEW_UI_EVENT_ID);
+ CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED(1800);
override fun getId(): Int {
return this.id
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
index 89fd72c..8870f28 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
@@ -17,7 +17,6 @@
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
enum class GetCredentialEvent(private val id: Int) : UiEventLogger.UiEventEnum {
@@ -58,7 +57,7 @@
CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342),
@UiEvent(doc = "The single tap biometric flow is launched.")
- CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(RESERVE_NEW_UI_EVENT_ID);
+ CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(1801);
override fun getId(): Int {
return this.id
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 59a511d..10e8246 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -311,18 +311,18 @@
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- try {
- // Delay committing the session by 100ms to fix a UI glitch while displaying the
- // Update-Owner change dialog on top of the Installing dialog
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ // Delay committing the session by 100ms to fix a UI glitch while displaying the
+ // Update-Owner change dialog on top of the Installing dialog
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ try {
session.commit(pendingIntent.getIntentSender());
- }, 100);
- } catch (Exception e) {
- Log.e(LOG_TAG, "Cannot install package: ", e);
- launchFailure(PackageInstaller.STATUS_FAILURE,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
- return;
- }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Cannot install package: ", e);
+ launchFailure(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ return;
+ }
+ }, 100);
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
diff --git a/packages/SettingsLib/Color/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml
index b0b9b10..7097523 100644
--- a/packages/SettingsLib/Color/res/values/colors.xml
+++ b/packages/SettingsLib/Color/res/values/colors.xml
@@ -62,4 +62,5 @@
<color name="settingslib_color_cyan400">#4ecde6</color>
<color name="settingslib_color_cyan300">#78d9ec</color>
<color name="settingslib_color_cyan100">#cbf0f8</color>
+ <color name="settingslib_color_charcoal">#171717</color>
</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 0447ef8..cf645f7 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -41,6 +41,9 @@
static {
HashMap<String, Integer> map = new HashMap<>();
map.put(
+ ".grey200",
+ R.color.settingslib_color_grey800);
+ map.put(
".grey600",
R.color.settingslib_color_grey400);
map.put(
@@ -67,6 +70,9 @@
map.put(
".red200",
R.color.settingslib_color_red500);
+ map.put(
+ ".cream",
+ R.color.settingslib_color_charcoal);
DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
index a6cc3a9..ea4480f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
@@ -49,6 +49,7 @@
object LottieColorUtils {
private val DARK_TO_LIGHT_THEME_COLOR_MAP = mapOf(
+ ".grey200" to R.color.settingslib_color_grey800,
".grey600" to R.color.settingslib_color_grey400,
".grey800" to R.color.settingslib_color_grey300,
".grey900" to R.color.settingslib_color_grey50,
@@ -58,6 +59,7 @@
".green400" to R.color.settingslib_color_green600,
".green200" to R.color.settingslib_color_green500,
".red200" to R.color.settingslib_color_red500,
+ ".cream" to R.color.settingslib_color_charcoal
)
@Composable
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
index 71072a5..d91c7e6 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
@@ -16,9 +16,9 @@
package com.android.settingslib.spa.widget.ui
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.longClick
@@ -63,9 +63,9 @@
@Test
fun onCopy_saveToClipboard() {
- val clipboardManager = context.getSystemService(ClipboardManager::class.java)!!
- clipboardManager.setPrimaryClip(ClipData.newPlainText("", ""))
+ var clipboardManager: ClipboardManager? = null
composeTestRule.setContent {
+ clipboardManager = LocalClipboardManager.current
CopyableBody(TEXT)
}
@@ -74,7 +74,7 @@
}
composeTestRule.onNodeWithText(context.getString(android.R.string.copy)).performClick()
- assertThat(clipboardManager.primaryClip!!.getItemAt(0).text.toString()).isEqualTo(TEXT)
+ assertThat(clipboardManager?.getText()?.text).isEqualTo(TEXT)
}
private companion object {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f83928d..03c2a83 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -418,6 +418,7 @@
VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.BEDTIME_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.BEDTIME_HARD_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.VIBRATE_FOR_ACTIVE_UNLOCK, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.DYNAMIC_COLOR_THEME_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.SCREENSHOT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.UPGRADE_DATA_MIGRATION_STATUS,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 04922d6..c8992c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,7 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
@@ -85,13 +86,13 @@
// FOR ACONFIGD TEST MISSION AND ROLLOUT
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
import android.util.proto.ProtoInputStream;
import android.aconfigd.Aconfigd.StorageRequestMessage;
import android.aconfigd.Aconfigd.StorageRequestMessages;
import android.aconfigd.Aconfigd.StorageReturnMessage;
import android.aconfigd.Aconfigd.StorageReturnMessages;
+import android.aconfigd.AconfigdClientSocket;
+import android.aconfigd.AconfigdFlagInfo;
import android.aconfigd.AconfigdJavaUtils;
import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
/**
@@ -265,6 +266,10 @@
@NonNull
private Map<String, Map<String, String>> mNamespaceDefaults;
+ // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+ @NonNull
+ private Map<String, AconfigdFlagInfo> mAconfigDefaultFlags;
+
public static final int SETTINGS_TYPE_GLOBAL = 0;
public static final int SETTINGS_TYPE_SYSTEM = 1;
public static final int SETTINGS_TYPE_SECURE = 2;
@@ -334,8 +339,13 @@
+ settingTypeToString(getTypeFromKey(key)) + "]";
}
- public SettingsState(Context context, Object lock, File file, int key,
- int maxBytesPerAppPackage, Looper looper) {
+ public SettingsState(
+ Context context,
+ Object lock,
+ File file,
+ int key,
+ int maxBytesPerAppPackage,
+ Looper looper) {
// It is important that we use the same lock as the settings provider
// to ensure multiple mutations on this state are atomically persisted
// as the async persistence should be blocked while we make changes.
@@ -353,12 +363,15 @@
mPackageToMemoryUsage = null;
}
- mHistoricalOperations = Build.IS_DEBUGGABLE
- ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
+ mHistoricalOperations =
+ Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
mNamespaceDefaults = new HashMap<>();
+ mAconfigDefaultFlags = new HashMap<>();
ProtoOutputStream requests = null;
+ Map<String, AconfigdFlagInfo> aconfigFlagMap = new HashMap<>();
+
synchronized (mLock) {
readStateSyncLocked();
@@ -375,39 +388,114 @@
}
}
+ if (enableAconfigStorageDaemon()) {
+ if (isConfigSettingsKey(mKey)) {
+ aconfigFlagMap = getAllAconfigFlagsFromSettings();
+ }
+ }
+
if (isConfigSettingsKey(mKey)) {
- requests = handleBulkSyncToNewStorage();
+ requests = handleBulkSyncToNewStorage(aconfigFlagMap);
}
}
- if (requests != null) {
- LocalSocket client = new LocalSocket();
- try{
- client.connect(new LocalSocketAddress(
- "aconfigd", LocalSocketAddress.Namespace.RESERVED));
- Slog.d(LOG_TAG, "connected to aconfigd socket");
- } catch (IOException ioe) {
- Slog.e(LOG_TAG, "failed to connect to aconfigd socket", ioe);
- return;
+ if (enableAconfigStorageDaemon()) {
+ if (isConfigSettingsKey(mKey)){
+ AconfigdClientSocket localSocket = AconfigdJavaUtils.getAconfigdClientSocket();
+ if (requests != null) {
+ InputStream res = localSocket.send(requests.getBytes());
+ if (res == null) {
+ Slog.w(LOG_TAG, "Bulk sync request to acongid failed.");
+ }
+ }
+ // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+ if (mSettings.get("aconfigd_marker/bulk_synced").value.equals("true")
+ && requests == null) {
+ Map<String, AconfigdFlagInfo> aconfigdFlagMap =
+ AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket);
+ compareFlagValueInNewStorage(
+ aconfigFlagMap,
+ mAconfigDefaultFlags,
+ aconfigdFlagMap);
+ }
}
- AconfigdJavaUtils.sendAconfigdRequests(client, requests);
}
}
- // TODO(b/341764371): migrate aconfig flag push to GMS core
- public static class FlagOverrideToSync {
- public String packageName;
- public String flagName;
- public String flagValue;
- public boolean isLocal;
+ // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+ public int compareFlagValueInNewStorage(
+ Map<String, AconfigdFlagInfo> settingFlagMap,
+ Map<String, AconfigdFlagInfo> defaultFlagMap,
+ Map<String, AconfigdFlagInfo> aconfigdFlagMap) {
+
+ // Get all defaults from the default map. The mSettings may not contain
+ // all flags, since it only contains updated flags.
+ int diffNum = 0;
+ for (Map.Entry<String, AconfigdFlagInfo> entry : defaultFlagMap.entrySet()) {
+ String key = entry.getKey();
+ AconfigdFlagInfo flag = entry.getValue();
+ if (settingFlagMap.containsKey(key)) {
+ flag.merge(settingFlagMap.get(key));
+ }
+
+ AconfigdFlagInfo aconfigdFlag = aconfigdFlagMap.get(key);
+ if (aconfigdFlag == null) {
+ Slog.w(LOG_TAG, String.format("Flag %s is missing from aconfigd", key));
+ diffNum++;
+ continue;
+ }
+ String diff = flag.dumpDiff(aconfigdFlag);
+ if (!diff.isEmpty()) {
+ Slog.w(
+ LOG_TAG,
+ String.format(
+ "Flag %s is different in Settings and aconfig: %s", key, diff));
+ diffNum++;
+ }
+ }
+
+ for (String key : aconfigdFlagMap.keySet()) {
+ if (defaultFlagMap.containsKey(key)) continue;
+ Slog.w(LOG_TAG, String.format("Flag %s is missing from Settings", key));
+ diffNum++;
+ }
+
+ if (diffNum == 0) {
+ Slog.i(LOG_TAG, "Settings and new storage have same flags.");
+ }
+ return diffNum;
+ }
+
+ @GuardedBy("mLock")
+ public Map<String, AconfigdFlagInfo> getAllAconfigFlagsFromSettings() {
+ Map<String, AconfigdFlagInfo> ret = new HashMap<>();
+ int numSettings = mSettings.size();
+ int num_requests = 0;
+ for (int i = 0; i < numSettings; i++) {
+ String name = mSettings.keyAt(i);
+ Setting setting = mSettings.valueAt(i);
+ AconfigdFlagInfo flag =
+ getFlagOverrideToSync(name, setting.getValue());
+ if (flag == null) {
+ continue;
+ }
+ String fullFlagName = flag.getFullFlagName();
+ AconfigdFlagInfo prev = ret.putIfAbsent(fullFlagName,flag);
+ if (prev != null) {
+ prev.merge(flag);
+ }
+ ++num_requests;
+ }
+ Slog.i(LOG_TAG, num_requests + " flag override requests created");
+ return ret;
}
// TODO(b/341764371): migrate aconfig flag push to GMS core
@VisibleForTesting
@GuardedBy("mLock")
- public FlagOverrideToSync getFlagOverrideToSync(String name, String value) {
+ public AconfigdFlagInfo getFlagOverrideToSync(String name, String value) {
int slashIdx = name.indexOf("/");
- if (slashIdx <= 0 || slashIdx >= name.length()-1) {
+ if (slashIdx <= 0 || slashIdx >= name.length() - 1) {
Slog.e(LOG_TAG, "invalid flag name " + name);
return null;
}
@@ -430,8 +518,9 @@
}
String aconfigName = namespace + "/" + fullFlagName;
- boolean isAconfig = mNamespaceDefaults.containsKey(namespace)
- && mNamespaceDefaults.get(namespace).containsKey(aconfigName);
+ boolean isAconfig =
+ mNamespaceDefaults.containsKey(namespace)
+ && mNamespaceDefaults.get(namespace).containsKey(aconfigName);
if (!isAconfig) {
return null;
}
@@ -443,25 +532,30 @@
return null;
}
- FlagOverrideToSync flag = new FlagOverrideToSync();
- flag.packageName = fullFlagName.substring(0, dotIdx);
- flag.flagName = fullFlagName.substring(dotIdx + 1);
- flag.isLocal = isLocal;
- flag.flagValue = value;
- return flag;
+ AconfigdFlagInfo.Builder builder = AconfigdFlagInfo.newBuilder()
+ .setPackageName(fullFlagName.substring(0, dotIdx))
+ .setFlagName(fullFlagName.substring(dotIdx + 1))
+ .setDefaultFlagValue(mNamespaceDefaults.get(namespace).get(aconfigName));
+
+ if (isLocal) {
+ builder.setHasLocalOverride(isLocal).setBootFlagValue(value).setLocalFlagValue(value);
+ } else {
+ builder.setHasServerOverride(true).setServerFlagValue(value).setBootFlagValue(value);
+ }
+ return builder.build();
}
// TODO(b/341764371): migrate aconfig flag push to GMS core
@VisibleForTesting
@GuardedBy("mLock")
- public ProtoOutputStream handleBulkSyncToNewStorage() {
+ public ProtoOutputStream handleBulkSyncToNewStorage(
+ Map<String, AconfigdFlagInfo> aconfigFlagMap) {
// get marker or add marker if it does not exist
final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced");
Setting markerSetting = mSettings.get(bulkSyncMarkerName);
if (markerSetting == null) {
- markerSetting = new Setting(
- bulkSyncMarkerName, "false", false, "aconfig", "aconfig");
+ markerSetting = new Setting(bulkSyncMarkerName, "false", false, "aconfig", "aconfig");
mSettings.put(bulkSyncMarkerName, markerSetting);
}
@@ -479,24 +573,19 @@
AconfigdJavaUtils.writeResetStorageRequest(requests);
// loop over all settings and add flag override requests
- final int numSettings = mSettings.size();
- int num_requests = 0;
- for (int i = 0; i < numSettings; i++) {
- String name = mSettings.keyAt(i);
- Setting setting = mSettings.valueAt(i);
- FlagOverrideToSync flag =
- getFlagOverrideToSync(name, setting.getValue());
- if (flag == null) {
- continue;
- }
- ++num_requests;
+ for (AconfigdFlagInfo flag : aconfigFlagMap.values()) {
+ String value =
+ flag.getHasLocalOverride()
+ ? flag.getLocalFlagValue()
+ : flag.getServerFlagValue();
AconfigdJavaUtils.writeFlagOverrideRequest(
- requests, flag.packageName, flag.flagName, flag.flagValue,
- flag.isLocal);
+ requests,
+ flag.getPackageName(),
+ flag.getFlagName(),
+ value,
+ flag.getHasLocalOverride());
}
- Slog.i(LOG_TAG, num_requests + " flag override requests created");
-
// mark sync has been done
markerSetting.value = "true";
scheduleWriteIfNeededLocked();
@@ -513,14 +602,14 @@
return null;
}
}
-
}
@GuardedBy("mLock")
private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
for (String fileName : filePaths) {
try (FileInputStream inputStream = new FileInputStream(fileName)) {
- loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults);
+ loadAconfigDefaultValues(
+ inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
} catch (IOException e) {
Slog.e(LOG_TAG, "failed to read protobuf", e);
}
@@ -566,21 +655,30 @@
@VisibleForTesting
@GuardedBy("mLock")
- public static void loadAconfigDefaultValues(byte[] fileContents,
- @NonNull Map<String, Map<String, String>> defaultMap) {
+ public static void loadAconfigDefaultValues(
+ byte[] fileContents,
+ @NonNull Map<String, Map<String, String>> defaultMap,
+ @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
try {
- parsed_flags parsedFlags =
- parsed_flags.parseFrom(fileContents);
+ parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents);
for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
if (!defaultMap.containsKey(flag.getNamespace())) {
Map<String, String> defaults = new HashMap<>();
defaultMap.put(flag.getNamespace(), defaults);
}
- String flagName = flag.getNamespace()
- + "/" + flag.getPackage() + "." + flag.getName();
- String flagValue = flag.getState() == flag_state.ENABLED
- ? "true" : "false";
+ String fullFlagName = flag.getPackage() + "." + flag.getName();
+ String flagName = flag.getNamespace() + "/" + fullFlagName;
+ String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false";
defaultMap.get(flag.getNamespace()).put(flagName, flagValue);
+ if (!flagInfoDefault.containsKey(fullFlagName)) {
+ flagInfoDefault.put(
+ fullFlagName,
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName(flag.getPackage())
+ .setFlagName(flag.getName())
+ .setDefaultFlagValue(flagValue)
+ .build());
+ }
}
} catch (IOException e) {
Slog.e(LOG_TAG, "failed to parse protobuf", e);
@@ -1646,7 +1744,6 @@
}
}
}
-
mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
fromSystem, id, isPreservedInRestore));
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 473955f..4ec170d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -619,6 +619,7 @@
Settings.Global.Wearable.COOLDOWN_MODE_ON,
Settings.Global.Wearable.BEDTIME_MODE,
Settings.Global.Wearable.BEDTIME_HARD_MODE,
+ Settings.Global.Wearable.VIBRATE_FOR_ACTIVE_UNLOCK,
Settings.Global.Wearable.LOCK_SCREEN_STATE,
Settings.Global.Wearable.DISABLE_AOD_WHILE_PLUGGED,
Settings.Global.Wearable.NETWORK_LOCATION_OPT_IN,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 244c8c4..256b999 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -24,13 +24,13 @@
import android.aconfig.Aconfig;
import android.aconfig.Aconfig.parsed_flag;
import android.aconfig.Aconfig.parsed_flags;
+import android.aconfigd.AconfigdFlagInfo;
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
-import com.android.providers.settings.SettingsState.FlagOverrideToSync;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -145,16 +145,32 @@
.setState(Aconfig.flag_state.ENABLED)
.setPermission(Aconfig.flag_permission.READ_WRITE))
.build();
+
+ AconfigdFlagInfo flag1 = AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setDefaultFlagValue("false")
+ .build();
+ AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag2")
+ .setDefaultFlagValue("true")
+ .build();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
synchronized (lock) {
Map<String, Map<String, String>> defaults = new HashMap<>();
- settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+ settingsState.loadAconfigDefaultValues(
+ flags.toByteArray(), defaults, flagInfoDefault);
Map<String, String> namespaceDefaults = defaults.get("test_namespace");
assertEquals(2, namespaceDefaults.keySet().size());
assertEquals("false", namespaceDefaults.get("test_namespace/com.android.flags.flag1"));
assertEquals("true", namespaceDefaults.get("test_namespace/com.android.flags.flag2"));
}
+
+ assertEquals(flag1, flagInfoDefault.get(flag1.getFullFlagName()));
+ assertEquals(flag2, flagInfoDefault.get(flag2.getFullFlagName()));
}
@Test
@@ -165,6 +181,8 @@
InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+
parsed_flags flags = parsed_flags
.newBuilder()
.addParsedFlag(parsed_flag
@@ -177,7 +195,8 @@
synchronized (lock) {
Map<String, Map<String, String>> defaults = new HashMap<>();
- settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+ settingsState.loadAconfigDefaultValues(
+ flags.toByteArray(), defaults, flagInfoDefault);
Map<String, String> namespaceDefaults = defaults.get("test_namespace");
assertEquals(null, namespaceDefaults);
@@ -204,10 +223,12 @@
.setState(Aconfig.flag_state.DISABLED)
.setPermission(Aconfig.flag_permission.READ_WRITE))
.build();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
synchronized (lock) {
Map<String, Map<String, String>> defaults = new HashMap<>();
- settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+ settingsState.loadAconfigDefaultValues(
+ flags.toByteArray(), defaults, flagInfoDefault);
settingsState.addAconfigDefaultValuesFromMap(defaults);
settingsState.insertSettingLocked("test_namespace/com.android.flags.flag5",
@@ -238,8 +259,10 @@
@Test
public void testInvalidAconfigProtoDoesNotCrash() {
Map<String, Map<String, String>> defaults = new HashMap<>();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
SettingsState settingsState = getSettingStateObject();
- settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes(), defaults);
+ settingsState.loadAconfigDefaultValues(
+ "invalid protobuf".getBytes(), defaults, flagInfoDefault);
}
@Test
@@ -759,6 +782,8 @@
Map<String, String> keyValues =
Map.of("test_namespace/com.android.flags.flag3", "true");
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+
parsed_flags flags = parsed_flags
.newBuilder()
.addParsedFlag(parsed_flag
@@ -774,7 +799,8 @@
synchronized (mLock) {
settingsState.loadAconfigDefaultValues(
- flags.toByteArray(), settingsState.getAconfigDefaultValues());
+ flags.toByteArray(),
+ settingsState.getAconfigDefaultValues(), flagInfoDefault);
List<String> updates =
settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
assertEquals(1, updates.size());
@@ -840,10 +866,13 @@
.setState(Aconfig.flag_state.DISABLED)
.setPermission(Aconfig.flag_permission.READ_WRITE))
.build();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
synchronized (mLock) {
settingsState.loadAconfigDefaultValues(
- flags.toByteArray(), settingsState.getAconfigDefaultValues());
+ flags.toByteArray(),
+ settingsState.getAconfigDefaultValues(),
+ flagInfoDefault);
List<String> updates =
settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
assertEquals(3, updates.size());
@@ -973,10 +1002,12 @@
.setState(Aconfig.flag_state.DISABLED)
.setPermission(Aconfig.flag_permission.READ_WRITE))
.build();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
synchronized (lock) {
Map<String, Map<String, String>> defaults = new HashMap<>();
- settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+ settingsState.loadAconfigDefaultValues(
+ flags.toByteArray(), defaults, flagInfoDefault);
Map<String, String> namespaceDefaults = defaults.get("test_namespace");
assertEquals(1, namespaceDefaults.keySet().size());
settingsState.addAconfigDefaultValuesFromMap(defaults);
@@ -991,22 +1022,28 @@
"some_namespace/some_flag", "false") == null);
// server override
- FlagOverrideToSync flag = settingsState.getFlagOverrideToSync(
+ AconfigdFlagInfo flag = settingsState.getFlagOverrideToSync(
"test_namespace/com.android.flags.flag1", "false");
assertTrue(flag != null);
- assertEquals(flag.packageName, "com.android.flags");
- assertEquals(flag.flagName, "flag1");
- assertEquals(flag.flagValue, "false");
- assertEquals(flag.isLocal, false);
+ assertEquals(flag.getPackageName(), "com.android.flags");
+ assertEquals(flag.getFlagName(), "flag1");
+ assertEquals("false", flag.getBootFlagValue());
+ assertEquals("false", flag.getServerFlagValue());
+ assertFalse(flag.getHasLocalOverride());
+ assertNull(flag.getLocalFlagValue());
+ assertEquals("false", flag.getDefaultFlagValue());
// local override
flag = settingsState.getFlagOverrideToSync(
"device_config_overrides/test_namespace:com.android.flags.flag1", "false");
assertTrue(flag != null);
- assertEquals(flag.packageName, "com.android.flags");
- assertEquals(flag.flagName, "flag1");
- assertEquals(flag.flagValue, "false");
- assertEquals(flag.isLocal, true);
+ assertEquals(flag.getPackageName(), "com.android.flags");
+ assertEquals(flag.getFlagName(), "flag1");
+ assertEquals("false", flag.getLocalFlagValue());
+ assertEquals("false", flag.getBootFlagValue());
+ assertTrue(flag.getHasLocalOverride());
+ assertNull(flag.getServerFlagValue());
+ assertEquals("false", flag.getDefaultFlagValue());
}
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -1020,18 +1057,25 @@
InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ Map<String, AconfigdFlagInfo> flags = new HashMap<>();
+ AconfigdFlagInfo flag = AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setBootFlagValue("true").build();
+ flags.put("com.android.flags/flag1", flag);
+
synchronized (lock) {
settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
"false", null, false, "aconfig");
// first bulk sync
- ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage();
+ ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
assertTrue(requests != null);
String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
assertEquals("true", value);
// send time should no longer bulk sync
- requests = settingsState.handleBulkSyncToNewStorage();
+ requests = settingsState.handleBulkSyncToNewStorage(flags);
assertTrue(requests == null);
value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
assertEquals("true", value);
@@ -1047,21 +1091,200 @@
InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ Map<String, AconfigdFlagInfo> flags = new HashMap<>();
synchronized (lock) {
settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
"true", null, false, "aconfig");
// when aconfigd is off, should change the marker to false
- ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage();
+ ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
assertTrue(requests == null);
String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
assertEquals("false", value);
// marker started with false value, after call, it should remain false
- requests = settingsState.handleBulkSyncToNewStorage();
+ requests = settingsState.handleBulkSyncToNewStorage(flags);
assertTrue(requests == null);
value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
assertEquals("false", value);
}
}
+
+ @Test
+ public void testGetAllAconfigFlagsFromSettings() throws Exception {
+ final Object lock = new Object();
+ final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
+ os.print(
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
+ + "<settings version=\"120\">"
+ + " <setting id=\"0\" name=\"test_namespace/com.android.flags.flag1\" "
+ + "value=\"false\" package=\"com.android.flags\" />"
+ + " <setting id=\"1\" name=\"device_config_overrides/test_namespace:com.android.flags.flag1\" "
+ + "value=\"true\" package=\"com.android.flags\" />"
+ + " <setting id=\"2\" name=\"device_config_overrides/test_namespace:com.android.flags.flag2\" "
+ + "value=\"true\" package=\"com.android.flags\" />"
+ + " <setting id=\"3\" name=\"test_namespace/com.android.flags.flag3\" "
+ + "value=\"true\" package=\"com.android.flags\" />"
+ + "</settings>");
+ os.close();
+
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+
+ SettingsState settingsState = new SettingsState(
+ InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+ Map<String, AconfigdFlagInfo> ret;
+ synchronized (lock) {
+ ret = settingsState.getAllAconfigFlagsFromSettings();
+ }
+
+ assertTrue(ret.isEmpty());
+
+ parsed_flags flags =
+ parsed_flags
+ .newBuilder()
+ .addParsedFlag(
+ parsed_flag
+ .newBuilder()
+ .setPackage("com.android.flags")
+ .setName("flag1")
+ .setNamespace("test_namespace")
+ .setDescription("test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.DISABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .addParsedFlag(
+ parsed_flag
+ .newBuilder()
+ .setPackage("com.android.flags")
+ .setName("flag2")
+ .setNamespace("test_namespace")
+ .setDescription("test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.DISABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .addParsedFlag(
+ parsed_flag
+ .newBuilder()
+ .setPackage("com.android.flags")
+ .setName("flag3")
+ .setNamespace("test_namespace")
+ .setDescription("test flag")
+ .addBug("12345678")
+ .setState(Aconfig.flag_state.DISABLED)
+ .setPermission(Aconfig.flag_permission.READ_WRITE))
+ .build();
+
+ Map<String, Map<String, String>> defaults = new HashMap<>();
+ Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+ synchronized (lock) {
+ settingsState.loadAconfigDefaultValues(
+ flags.toByteArray(), defaults, flagInfoDefault);
+ settingsState.addAconfigDefaultValuesFromMap(defaults);
+ ret = settingsState.getAllAconfigFlagsFromSettings();
+ }
+
+ AconfigdFlagInfo expectedFlag1 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setServerFlagValue("false")
+ .setLocalFlagValue("true")
+ .setDefaultFlagValue("false")
+ .setBootFlagValue("true")
+ .setHasServerOverride(true)
+ .setHasLocalOverride(true)
+ .setIsReadWrite(false)
+ .build();
+
+ AconfigdFlagInfo expectedFlag2 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag2")
+ .setLocalFlagValue("true")
+ .setDefaultFlagValue("false")
+ .setBootFlagValue("true")
+ .setHasLocalOverride(true)
+ .setHasServerOverride(false)
+ .setIsReadWrite(false)
+ .build();
+
+
+ AconfigdFlagInfo expectedFlag3 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag3")
+ .setServerFlagValue("true")
+ .setBootFlagValue("true")
+ .setDefaultFlagValue("false")
+ .setHasServerOverride(true)
+ .setIsReadWrite(false)
+ .build();
+
+ assertEquals(expectedFlag1, ret.get("com.android.flags.flag1"));
+ assertEquals(expectedFlag2, ret.get("com.android.flags.flag2"));
+ assertEquals(expectedFlag3, ret.get("com.android.flags.flag3"));
+ }
+
+ @Test
+ public void testCompareFlagValueInNewStorage() {
+ int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+ Object lock = new Object();
+ SettingsState settingsState =
+ new SettingsState(
+ InstrumentationRegistry.getContext(),
+ lock,
+ mSettingsFile,
+ configKey,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
+ Looper.getMainLooper());
+
+ AconfigdFlagInfo defaultFlag1 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setDefaultFlagValue("false")
+ .build();
+
+ AconfigdFlagInfo settingFlag1 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setServerFlagValue("true")
+ .setHasServerOverride(true)
+ .build();
+
+ AconfigdFlagInfo expectedFlag1 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag1")
+ .setBootFlagValue("true")
+ .setServerFlagValue("true")
+ .setDefaultFlagValue("false")
+ .setHasServerOverride(true)
+ .build();
+
+ Map<String, AconfigdFlagInfo> settingMap = new HashMap<>();
+ Map<String, AconfigdFlagInfo> aconfigdMap = new HashMap<>();
+ Map<String, AconfigdFlagInfo> defaultMap = new HashMap<>();
+
+ defaultMap.put("com.android.flags.flag1", defaultFlag1);
+ settingMap.put("com.android.flags.flag1", settingFlag1);
+ aconfigdMap.put("com.android.flags.flag1", expectedFlag1);
+
+ int ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+ assertEquals(0, ret);
+
+ AconfigdFlagInfo defaultFlag2 =
+ AconfigdFlagInfo.newBuilder()
+ .setPackageName("com.android.flags")
+ .setFlagName("flag2")
+ .setDefaultFlagValue("false")
+ .build();
+ defaultMap.put("com.android.flags.flag2", defaultFlag2);
+
+ ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+ assertEquals(1, ret);
+ }
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e940674..7a098ee 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,9 +78,42 @@
visibility: ["//visibility:private"],
}
+// Tests where robolectric conversion caused errors in SystemUITests at runtime
+filegroup {
+ name: "SystemUI-tests-broken-robofiles-sysui-run",
+ srcs: [
+ "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+ "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+ "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+ "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+ ],
+}
+
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
+ "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
+ "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+ "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
+ "tests/src/**/systemui/graphics/ImageLoaderTest.kt",
+ "tests/src/**/systemui/keyguard/KeyguardViewMediatorTest.java",
+ "tests/src/**/systemui/keyguard/LifecycleTest.java",
+ "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+ "tests/src/**/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt",
+ "tests/src/**/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt",
+ "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt",
+ "tests/src/**/systemui/lifecycle/RepeatWhenAttachedTest.kt",
+ "tests/src/**/systemui/log/LogBufferTest.kt",
+ "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+ "tests/src/**/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt",
+ "tests/src/**/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt",
"tests/src/**/systemui/util/LifecycleFragmentTest.java",
"tests/src/**/systemui/util/TestableAlertDialogTest.kt",
"tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
@@ -767,6 +800,7 @@
":SystemUI-tests-robofiles",
],
exclude_srcs: [
+ ":SystemUI-tests-broken-robofiles-sysui-run",
":SystemUI-tests-broken-robofiles-compile",
":SystemUI-tests-broken-robofiles-run",
],
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 57a7df28..4a4c3a5 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -743,16 +743,6 @@
}
flag {
- name: "trim_resources_with_background_trim_at_lock"
- namespace: "systemui"
- description: "Trim fonts and other caches when the device locks to lower memory consumption."
- bug: "322143614"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "dedicated_notif_inflation_thread"
namespace: "systemui"
description: "Create a separate background thread for inflating notifications"
@@ -1018,6 +1008,13 @@
}
flag {
+ name: "glanceable_hub_allow_keyguard_when_dreaming"
+ namespace: "systemui"
+ description: "Allows users to exit dream to keyguard with glanceable hub enabled"
+ bug: "343505271"
+}
+
+flag {
name: "new_touchpad_gestures_tutorial"
namespace: "systemui"
description: "Enables new interactive tutorial for learning touchpad gestures"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index a90f82e..c329384 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -1,9 +1,16 @@
package com.android.systemui.communal.ui.compose
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -13,12 +20,20 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.CommunalSwipeDetector
@@ -36,8 +51,10 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
import com.android.systemui.Flags.glanceableHubFullscreenSwipe
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -102,6 +119,10 @@
val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
val showGestureIndicator by
viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
+ val backgroundType by
+ viewModel.communalBackground.collectAsStateWithLifecycle(
+ initialValue = CommunalBackgroundType.DEFAULT
+ )
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
@@ -174,7 +195,7 @@
userActions =
mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
) {
- CommunalScene(colors, content)
+ CommunalScene(backgroundType, colors, content)
}
}
@@ -186,17 +207,87 @@
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
+ backgroundType: CommunalBackgroundType,
colors: CommunalColors,
content: CommunalContent,
modifier: Modifier = Modifier,
) {
- val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
-
- Box(
- modifier =
- Modifier.element(Communal.Elements.Scrim)
- .fillMaxSize()
- .background(Color(backgroundColor.toArgb())),
- )
+ Box(modifier = Modifier.element(Communal.Elements.Scrim).fillMaxSize()) {
+ when (backgroundType) {
+ CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors)
+ CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
+ CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
+ }
+ }
with(content) { Content(modifier = modifier) }
}
+
+/** Default background of the hub, a single color */
+@Composable
+private fun BoxScope.DefaultBackground(
+ colors: CommunalColors,
+) {
+ val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
+ Box(
+ modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())),
+ )
+}
+
+/** Experimental hub background, static linear gradient */
+@Composable
+private fun BoxScope.StaticLinearGradient() {
+ val colors = LocalAndroidColorScheme.current
+ Box(
+ Modifier.matchParentSize()
+ .background(
+ Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer)),
+ )
+ )
+ BackgroundTopScrim()
+}
+
+/** Experimental hub background, animated linear gradient */
+@Composable
+private fun BoxScope.AnimatedLinearGradient() {
+ val colors = LocalAndroidColorScheme.current
+ Box(
+ Modifier.matchParentSize()
+ .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer))
+ )
+ BackgroundTopScrim()
+}
+
+/** Scrim placed on top of the background in order to dim/bright colors */
+@Composable
+private fun BoxScope.BackgroundTopScrim() {
+ val darkTheme = isSystemInDarkTheme()
+ val scrimOnTopColor = if (darkTheme) Color.Black else Color.White
+ Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
+}
+
+/** Modifier which sets the background of a composable to an animated gradient */
+@Composable
+private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed {
+ var size by remember { mutableStateOf(IntSize.Zero) }
+ val transition = rememberInfiniteTransition(label = "scrim background")
+ val startOffsetX by
+ transition.animateFloat(
+ initialValue = -size.width.toFloat(),
+ targetValue = size.width.toFloat(),
+ animationSpec =
+ infiniteRepeatable(
+ animation = tween(durationMillis = 5_000, easing = LinearEasing),
+ repeatMode = RepeatMode.Reverse,
+ ),
+ label = "scrim start offset"
+ )
+ background(
+ brush =
+ Brush.linearGradient(
+ colors = colors,
+ start = Offset(startOffsetX, 0f),
+ end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()),
+ )
+ )
+ .onGloballyPositioned { size = it.size }
+}
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 a730206..d776e2a 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
@@ -14,17 +14,28 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalLayoutApi::class)
+
package com.android.systemui.shade.ui.composable
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
+import androidx.compose.foundation.layout.waterfall
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
@@ -35,12 +46,12 @@
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.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.scene.shared.model.Scenes
@@ -59,8 +70,6 @@
content: @Composable () -> Unit,
) {
val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
- val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
- val isPanelFullWidth = widthSizeClass == WindowWidthSizeClass.Compact
Box(modifier) {
if (backgroundScene == Scenes.Lockscreen) {
@@ -73,10 +82,7 @@
Scrim(onClicked = viewModel::onScrimClicked)
Row(
- modifier =
- Modifier.fillMaxSize().thenIf(!isPanelFullWidth) {
- Modifier.padding(OverlayShade.Dimensions.ScrimContentPadding)
- },
+ modifier = Modifier.fillMaxSize().panelPadding(),
horizontalArrangement = horizontalArrangement,
) {
Panel(
@@ -138,6 +144,42 @@
)
}
+@Composable
+private fun Modifier.panelPadding(): Modifier {
+ val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
+ val systemBars = WindowInsets.systemBarsIgnoringVisibility
+ val displayCutout = WindowInsets.displayCutout
+ val waterfall = WindowInsets.waterfall
+ val contentPadding = PaddingValues(all = OverlayShade.Dimensions.ScrimContentPadding)
+
+ val combinedPadding =
+ combinePaddings(
+ systemBars.asPaddingValues(),
+ displayCutout.asPaddingValues(),
+ waterfall.asPaddingValues(),
+ contentPadding
+ )
+
+ return if (widthSizeClass == WindowWidthSizeClass.Compact) {
+ padding(bottom = combinedPadding.calculateBottomPadding())
+ } else {
+ padding(combinedPadding)
+ }
+}
+
+/** Creates a union of [paddingValues] by using the max padding of each edge. */
+@Composable
+private fun combinePaddings(vararg paddingValues: PaddingValues): PaddingValues {
+ val layoutDirection = LocalLayoutDirection.current
+
+ return PaddingValues(
+ start = paddingValues.maxOfOrNull { it.calculateStartPadding(layoutDirection) } ?: 0.dp,
+ top = paddingValues.maxOfOrNull { it.calculateTopPadding() } ?: 0.dp,
+ end = paddingValues.maxOfOrNull { it.calculateEndPadding(layoutDirection) } ?: 0.dp,
+ bottom = paddingValues.maxOfOrNull { it.calculateBottomPadding() } ?: 0.dp
+ )
+}
+
object OverlayShade {
object Elements {
val Scrim = ElementKey("OverlayShadeScrim", scenePicker = LowestZIndexScenePicker)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
index da29d58..48af8cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
@@ -16,16 +16,13 @@
package com.android.systemui.volume.panel.component.spatialaudio
-import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
-import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
-import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioPopup
+import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioComponent
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@@ -40,14 +37,8 @@
criteria: SpatialAudioAvailabilityCriteria
): ComponentAvailabilityCriteria
- companion object {
-
- @Provides
- @IntoMap
- @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
- fun provideVolumePanelUiComponent(
- viewModel: SpatialAudioViewModel,
- popup: SpatialAudioPopup,
- ): VolumePanelUiComponent = ButtonComponent(viewModel.spatialAudioButton, popup::show)
- }
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
+ fun bindVolumePanelUiComponent(component: SpatialAudioComponent): VolumePanelUiComponent
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt
new file mode 100644
index 0000000..2d89b5c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.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.volume.panel.component.spatialaudio.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+/** [ComposeVolumePanelUiComponent] that represents spatial audio button in the Volume Panel. */
+class SpatialAudioComponent
+@Inject
+constructor(
+ private val viewModel: SpatialAudioViewModel,
+ private val popup: SpatialAudioPopup,
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val shouldUsePopup by viewModel.shouldUsePopup.collectAsStateWithLifecycle()
+
+ val buttonComponent: ComposeVolumePanelUiComponent =
+ remember(shouldUsePopup) {
+ if (shouldUsePopup) {
+ ButtonComponent(viewModel.spatialAudioButton, popup::show)
+ } else {
+ ToggleButtonComponent(viewModel.spatialAudioButton) {
+ if (it) {
+ viewModel.setEnabled(SpatialAudioEnabledModel.SpatialAudioEnabled)
+ } else {
+ viewModel.setEnabled(SpatialAudioEnabledModel.Disabled)
+ }
+ }
+ }
+ }
+ with(buttonComponent) { Content(modifier) }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 5d1a7c5..7fd3a176 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -27,11 +27,12 @@
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceIn
-import androidx.compose.ui.util.lerp
+import androidx.compose.ui.util.fastLastOrNull
+import kotlin.math.roundToInt
/**
* A [State] whose [value] is animated.
@@ -74,7 +75,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Int> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedIntType, canOverflow)
}
/**
@@ -88,7 +89,19 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Int> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedIntType, canOverflow)
+}
+
+private object SharedIntType : SharedValueType<Int, Int> {
+ override val unspecifiedValue: Int = Int.MIN_VALUE
+ override val zeroDeltaValue: Int = 0
+
+ override fun lerp(a: Int, b: Int, progress: Float): Int =
+ androidx.compose.ui.util.lerp(a, b, progress)
+
+ override fun diff(a: Int, b: Int): Int = a - b
+
+ override fun addWeighted(a: Int, b: Int, bWeight: Float): Int = (a + b * bWeight).roundToInt()
}
/**
@@ -102,7 +115,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Float> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedFloatType, canOverflow)
}
/**
@@ -116,7 +129,19 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Float> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedFloatType, canOverflow)
+}
+
+private object SharedFloatType : SharedValueType<Float, Float> {
+ override val unspecifiedValue: Float = Float.MIN_VALUE
+ override val zeroDeltaValue: Float = 0f
+
+ override fun lerp(a: Float, b: Float, progress: Float): Float =
+ androidx.compose.ui.util.lerp(a, b, progress)
+
+ override fun diff(a: Float, b: Float): Float = a - b
+
+ override fun addWeighted(a: Float, b: Float, bWeight: Float): Float = a + b * bWeight
}
/**
@@ -130,7 +155,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Dp> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedDpType, canOverflow)
}
/**
@@ -144,7 +169,20 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Dp> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedDpType, canOverflow)
+}
+
+private object SharedDpType : SharedValueType<Dp, Dp> {
+ override val unspecifiedValue: Dp = Dp.Unspecified
+ override val zeroDeltaValue: Dp = 0.dp
+
+ override fun lerp(a: Dp, b: Dp, progress: Float): Dp {
+ return androidx.compose.ui.unit.lerp(a, b, progress)
+ }
+
+ override fun diff(a: Dp, b: Dp): Dp = a - b
+
+ override fun addWeighted(a: Dp, b: Dp, bWeight: Float): Dp = a + b * bWeight
}
/**
@@ -157,7 +195,7 @@
value: Color,
key: ValueKey,
): AnimatedState<Color> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow = false)
+ return animateSceneValueAsState(value, key, SharedColorType, canOverflow = false)
}
/**
@@ -170,9 +208,56 @@
value: Color,
key: ValueKey,
): AnimatedState<Color> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow = false)
+ return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
}
+private object SharedColorType : SharedValueType<Color, ColorDelta> {
+ override val unspecifiedValue: Color = Color.Unspecified
+ override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f)
+
+ override fun lerp(a: Color, b: Color, progress: Float): Color {
+ return androidx.compose.ui.graphics.lerp(a, b, progress)
+ }
+
+ override fun diff(a: Color, b: Color): ColorDelta {
+ // Similar to lerp, we convert colors to the Oklab color space to perform operations on
+ // colors.
+ val aOklab = a.convert(ColorSpaces.Oklab)
+ val bOklab = b.convert(ColorSpaces.Oklab)
+ return ColorDelta(
+ red = aOklab.red - bOklab.red,
+ green = aOklab.green - bOklab.green,
+ blue = aOklab.blue - bOklab.blue,
+ alpha = aOklab.alpha - bOklab.alpha,
+ )
+ }
+
+ override fun addWeighted(a: Color, b: ColorDelta, bWeight: Float): Color {
+ val aOklab = a.convert(ColorSpaces.Oklab)
+ return Color(
+ red = aOklab.red + b.red * bWeight,
+ green = aOklab.green + b.green * bWeight,
+ blue = aOklab.blue + b.blue * bWeight,
+ alpha = aOklab.alpha + b.alpha * bWeight,
+ colorSpace = ColorSpaces.Oklab,
+ )
+ .convert(aOklab.colorSpace)
+ }
+}
+
+/**
+ * Represents the diff between two colors in the same color space.
+ *
+ * Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
+ * is internal.
+ */
+private class ColorDelta(
+ val red: Float,
+ val green: Float,
+ val blue: Float,
+ val alpha: Float,
+)
+
@Composable
internal fun <T> animateSharedValueAsState(
layoutImpl: SceneTransitionLayoutImpl,
@@ -180,23 +265,22 @@
element: ElementKey?,
key: ValueKey,
value: T,
- lerp: (T, T, Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T> {
DisposableEffect(layoutImpl, scene, element, key) {
// Create the associated maps that hold the current value for each (element, scene) pair.
val valueMap = layoutImpl.sharedValues.getOrPut(key) { mutableMapOf() }
- val sceneToValueMap =
- valueMap.getOrPut(element) { SnapshotStateMap<SceneKey, Any>() }
- as SnapshotStateMap<SceneKey, T>
- sceneToValueMap[scene] = value
+ val sharedValue = valueMap.getOrPut(element) { SharedValue(type) } as SharedValue<T, *>
+ val targetValues = sharedValue.targetValues
+ targetValues[scene] = value
onDispose {
// Remove the value associated to the current scene, and eventually remove the maps if
// they are empty.
- sceneToValueMap.remove(scene)
+ targetValues.remove(scene)
- if (sceneToValueMap.isEmpty() && valueMap[element] === sceneToValueMap) {
+ if (targetValues.isEmpty() && valueMap[element] === sharedValue) {
valueMap.remove(element)
if (valueMap.isEmpty() && layoutImpl.sharedValues[key] === valueMap) {
@@ -208,34 +292,25 @@
// Update the current value. Note that side effects run after disposable effects, so we know
// that the associated maps were created at this point.
- SideEffect { sceneToValueMap<T>(layoutImpl, key, element)[scene] = value }
-
- return remember(layoutImpl, scene, element, lerp, canOverflow) {
- object : AnimatedState<T> {
- override val value: T
- get() = value(layoutImpl, scene, element, key, lerp, canOverflow)
-
- @Composable
- override fun unsafeCompositionState(initialValue: T): State<T> {
- val state = remember { mutableStateOf(initialValue) }
-
- val animatedState = this
- LaunchedEffect(animatedState) {
- snapshotFlow { animatedState.value }.collect { state.value = it }
- }
-
- return state
- }
+ SideEffect {
+ if (value == type.unspecifiedValue) {
+ error("value is equal to $value, which is the undefined value for this type.")
}
+
+ sharedValue<T, Any>(layoutImpl, key, element).targetValues[scene] = value
+ }
+
+ return remember(layoutImpl, scene, element, canOverflow) {
+ AnimatedStateImpl<T, Any>(layoutImpl, scene, element, key, canOverflow)
}
}
-private fun <T> sceneToValueMap(
+private fun <T, Delta> sharedValue(
layoutImpl: SceneTransitionLayoutImpl,
key: ValueKey,
element: ElementKey?
-): MutableMap<SceneKey, T> {
- return layoutImpl.sharedValues[key]?.get(element)?.let { it as SnapshotStateMap<SceneKey, T> }
+): SharedValue<T, Delta> {
+ return layoutImpl.sharedValues[key]?.get(element)?.let { it as SharedValue<T, Delta> }
?: error(valueReadTooEarlyMessage(key))
}
@@ -244,62 +319,155 @@
"means that you are reading it during composition, which you should not do. See the " +
"documentation of AnimatedState for more information."
-private fun <T> value(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: SceneKey,
- element: ElementKey?,
- key: ValueKey,
- lerp: (T, T, Float) -> T,
- canOverflow: Boolean,
-): T {
- return valueOrNull(layoutImpl, scene, element, key, lerp, canOverflow)
- ?: error(valueReadTooEarlyMessage(key))
+internal class SharedValue<T, Delta>(
+ val type: SharedValueType<T, Delta>,
+) {
+ /** The target value of this shared value for each scene. */
+ val targetValues = SnapshotStateMap<SceneKey, T>()
+
+ /** The last value of this shared value. */
+ var lastValue: T = type.unspecifiedValue
+
+ /** The value of this shared value before the last interruption (if any). */
+ var valueBeforeInterruption: T = type.unspecifiedValue
+
+ /** The delta value to add to this shared value to have smoother interruptions. */
+ var valueInterruptionDelta = type.zeroDeltaValue
+
+ /** The last transition that was used when the value of this shared state. */
+ var lastTransition: TransitionState.Transition? = null
}
-private fun <T> valueOrNull(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: SceneKey,
- element: ElementKey?,
- key: ValueKey,
- lerp: (T, T, Float) -> T,
- canOverflow: Boolean,
-): T? {
- val sceneToValueMap = sceneToValueMap<T>(layoutImpl, key, element)
- fun sceneValue(scene: SceneKey): T? = sceneToValueMap[scene]
+private class AnimatedStateImpl<T, Delta>(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val scene: SceneKey,
+ private val element: ElementKey?,
+ private val key: ValueKey,
+ private val canOverflow: Boolean,
+) : AnimatedState<T> {
+ override val value: T
+ get() = value()
- return when (val transition = layoutImpl.state.transitionState) {
- is TransitionState.Idle -> sceneValue(transition.currentScene)
- is TransitionState.Transition -> {
- // Note: no need to check for transition ready here given that all target values are
- // defined during composition, we should already have the correct values to interpolate
- // between here.
- val fromValue = sceneValue(transition.fromScene)
- val toValue = sceneValue(transition.toScene)
- if (fromValue != null && toValue != null) {
- if (fromValue == toValue) {
- // Optimization: avoid reading progress if the values are the same, so we don't
- // relayout/redraw for nothing.
- fromValue
- } else {
- // In the case of bouncing, if the value remains constant during the overscroll,
- // we should use the value of the scene we are bouncing around.
- if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
- val bouncingScene = transition.bouncingScene
- if (bouncingScene != null) {
- return sceneValue(bouncingScene)
- }
+ private fun value(): T {
+ val sharedValue = sharedValue<T, Delta>(layoutImpl, key, element)
+ val transition = transition(sharedValue)
+ val value: T =
+ valueOrNull(sharedValue, transition)
+ // TODO(b/311600838): Remove this. We should not have to fallback to the current
+ // scene value, but we have to because code of removed nodes can still run if they
+ // are placed with a graphics layer.
+ ?: sharedValue[scene]
+ ?: error(valueReadTooEarlyMessage(key))
+ val interruptedValue = computeInterruptedValue(sharedValue, transition, value)
+ sharedValue.lastValue = interruptedValue
+ return interruptedValue
+ }
+
+ private operator fun SharedValue<T, *>.get(scene: SceneKey): T? = targetValues[scene]
+
+ private fun valueOrNull(
+ sharedValue: SharedValue<T, *>,
+ transition: TransitionState.Transition?,
+ ): T? {
+ if (transition == null) {
+ return sharedValue[layoutImpl.state.transitionState.currentScene]
+ }
+
+ val fromValue = sharedValue[transition.fromScene]
+ val toValue = sharedValue[transition.toScene]
+ return if (fromValue != null && toValue != null) {
+ if (fromValue == toValue) {
+ // Optimization: avoid reading progress if the values are the same, so we don't
+ // relayout/redraw for nothing.
+ fromValue
+ } else {
+ // In the case of bouncing, if the value remains constant during the overscroll, we
+ // should use the value of the scene we are bouncing around.
+ if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
+ val bouncingScene = transition.bouncingScene
+ if (bouncingScene != null) {
+ return sharedValue[bouncingScene]
}
-
- val progress =
- if (canOverflow) transition.progress
- else transition.progress.fastCoerceIn(0f, 1f)
- lerp(fromValue, toValue, progress)
}
- } else fromValue ?: toValue
+
+ val progress =
+ if (canOverflow) transition.progress
+ else transition.progress.fastCoerceIn(0f, 1f)
+ sharedValue.type.lerp(fromValue, toValue, progress)
+ }
+ } else fromValue ?: toValue
+ }
+
+ private fun transition(sharedValue: SharedValue<T, Delta>): TransitionState.Transition? {
+ val targetValues = sharedValue.targetValues
+ val transition =
+ if (element != null) {
+ layoutImpl.elements[element]?.sceneStates?.let { sceneStates ->
+ layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+ transition.fromScene in sceneStates || transition.toScene in sceneStates
+ }
+ }
+ } else {
+ layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+ transition.fromScene in targetValues || transition.toScene in targetValues
+ }
+ }
+
+ val previousTransition = sharedValue.lastTransition
+ sharedValue.lastTransition = transition
+
+ if (transition != previousTransition && transition != null && previousTransition != null) {
+ // The previous transition was interrupted by another transition.
+ sharedValue.valueBeforeInterruption = sharedValue.lastValue
+ sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+ } else if (transition == null && previousTransition != null) {
+ // The transition was just finished.
+ sharedValue.valueBeforeInterruption = sharedValue.type.unspecifiedValue
+ sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+ }
+
+ return transition
+ }
+
+ /**
+ * Compute what [value] should be if we take the
+ * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into
+ * account.
+ */
+ private fun computeInterruptedValue(
+ sharedValue: SharedValue<T, Delta>,
+ transition: TransitionState.Transition?,
+ value: T,
+ ): T {
+ val type = sharedValue.type
+ if (sharedValue.valueBeforeInterruption != type.unspecifiedValue) {
+ sharedValue.valueInterruptionDelta =
+ type.diff(sharedValue.valueBeforeInterruption, value)
+ sharedValue.valueBeforeInterruption = type.unspecifiedValue
+ }
+
+ val delta = sharedValue.valueInterruptionDelta
+ return if (delta == type.zeroDeltaValue || transition == null) {
+ value
+ } else {
+ val interruptionProgress = transition.interruptionProgress(layoutImpl)
+ if (interruptionProgress == 0f) {
+ value
+ } else {
+ type.addWeighted(value, delta, interruptionProgress)
+ }
}
}
- // TODO(b/311600838): Remove this. We should not have to fallback to the current scene value,
- // but we have to because code of removed nodes can still run if they are placed with a graphics
- // layer.
- ?: sceneValue(scene)
+
+ @Composable
+ override fun unsafeCompositionState(initialValue: T): State<T> {
+ val state = remember { mutableStateOf(initialValue) }
+
+ val animatedState = this
+ LaunchedEffect(animatedState) {
+ snapshotFlow { animatedState.value }.collect { state.value = it }
+ }
+
+ return state
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 1f81245..e9633c2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -148,9 +148,9 @@
val swipes = computeSwipes(fromScene, startedPosition, pointersDown)
val result =
swipes.findUserActionResult(fromScene, overSlop, true)
- // As we were unable to locate a valid target scene, the initial SwipeTransition
- // cannot be defined. Consequently, a simple NoOp Controller will be returned.
- ?: return NoOpDragController
+ // As we were unable to locate a valid target scene, the initial SwipeTransition
+ // cannot be defined. Consequently, a simple NoOp Controller will be returned.
+ ?: return NoOpDragController
return updateDragController(
swipes = swipes,
@@ -521,6 +521,7 @@
}
return SwipeTransition(
+ layoutImpl = layoutImpl,
layoutState = layoutState,
coroutineScope = coroutineScope,
key = result.transitionKey,
@@ -534,6 +535,7 @@
private fun SwipeTransition(old: SwipeTransition): SwipeTransition {
return SwipeTransition(
+ layoutImpl = old.layoutImpl,
layoutState = old.layoutState,
coroutineScope = old.coroutineScope,
key = old.key,
@@ -550,6 +552,7 @@
}
private class SwipeTransition(
+ val layoutImpl: SceneTransitionLayoutImpl,
val layoutState: BaseSceneTransitionLayoutState,
val coroutineScope: CoroutineScope,
val key: TransitionKey?,
@@ -607,6 +610,12 @@
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
+ override val density: Float
+ get() = layoutImpl.density.density
+
+ override val fontScale: Float
+ get() = layoutImpl.density.fontScale
+
override val absoluteDistance: Float
get() = distance().absoluteValue
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 4b20aca..be005ea 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -77,7 +77,7 @@
override fun <T> animateElementValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean
): AnimatedState<T> {
return animateSharedValueAsState(
@@ -86,7 +86,7 @@
element,
key,
value,
- lerp,
+ type,
canOverflow,
)
}
@@ -184,8 +184,7 @@
fromSceneZIndex = layoutImpl.scenes.getValue(transition.fromScene).zIndex,
toSceneZIndex = layoutImpl.scenes.getValue(transition.toScene).zIndex,
) != null
- }
- ?: return false
+ } ?: return false
// Always compose movable elements in the scene picked by their scene picker.
return shouldDrawOrComposeSharedElement(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 6fef33c..936f4ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -24,7 +24,6 @@
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.platform.testTag
@@ -69,7 +68,6 @@
}
@Composable
- @OptIn(ExperimentalComposeUiApi::class)
fun Content(modifier: Modifier = Modifier) {
Box(
modifier
@@ -96,6 +94,7 @@
private val layoutImpl: SceneTransitionLayoutImpl,
private val scene: Scene,
) : SceneScope, ElementStateScope by layoutImpl.elementStateScope {
+ override val sceneKey: SceneKey = scene.key
override val layoutState: SceneTransitionLayoutState = layoutImpl.state
override fun Modifier.element(key: ElementKey): Modifier {
@@ -124,7 +123,7 @@
override fun <T> animateSceneValueAsState(
value: T,
key: ValueKey,
- lerp: (T, T, Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean
): AnimatedState<T> {
return animateSharedValueAsState(
@@ -133,7 +132,7 @@
element = null,
key = key,
value = value,
- lerp = lerp,
+ type = type,
canOverflow = canOverflow,
)
}
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 cf8c584..2946b04 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
@@ -167,6 +167,9 @@
@Stable
@ElementDsl
interface BaseSceneScope : ElementStateScope {
+ /** The key of this scene. */
+ val sceneKey: SceneKey
+
/** The state of the [SceneTransitionLayout] in which this scene is contained. */
val layoutState: SceneTransitionLayoutState
@@ -285,9 +288,7 @@
*
* @param value the value of this shared value in the current scene.
* @param key the key of this shared value.
- * @param lerp the *linear* interpolation function that should be used to interpolate between
- * two different values. Note that it has to be linear because the [fraction] passed to this
- * interpolator is already interpolated.
+ * @param type the [SharedValueType] of this animated value.
* @param canOverflow whether this value can overflow past the values it is interpolated
* between, for instance because the transition is animated using a bouncy spring.
* @see animateSceneIntAsState
@@ -299,11 +300,39 @@
fun <T> animateSceneValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T>
}
+/**
+ * The type of a shared value animated using [ElementScope.animateElementValueAsState] or
+ * [SceneScope.animateSceneValueAsState].
+ */
+@Stable
+interface SharedValueType<T, Delta> {
+ /** The unspecified value for this type. */
+ val unspecifiedValue: T
+
+ /**
+ * The zero value of this type. It should be equal to what [diff(x, x)] returns for any value of
+ * x.
+ */
+ val zeroDeltaValue: Delta
+
+ /**
+ * Return the linear interpolation of [a] and [b] at the given [progress], i.e. `a + (b - a) *
+ * progress`.
+ */
+ fun lerp(a: T, b: T, progress: Float): T
+
+ /** Return `a - b`. */
+ fun diff(a: T, b: T): Delta
+
+ /** Return `a + b * bWeight`. */
+ fun addWeighted(a: T, b: Delta, bWeight: Float): T
+}
+
@Stable
@ElementDsl
interface ElementScope<ContentScope> {
@@ -312,9 +341,7 @@
*
* @param value the value of this shared value in the current scene.
* @param key the key of this shared value.
- * @param lerp the *linear* interpolation function that should be used to interpolate between
- * two different values. Note that it has to be linear because the [fraction] passed to this
- * interpolator is already interpolated.
+ * @param type the [SharedValueType] of this animated value.
* @param canOverflow whether this value can overflow past the values it is interpolated
* between, for instance because the transition is animated using a bouncy spring.
* @see animateElementIntAsState
@@ -326,7 +353,7 @@
fun <T> animateElementValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T>
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index c614265..5fa7c87 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -85,15 +85,14 @@
* The different values of a shared value keyed by a a [ValueKey] and the different elements and
* scenes it is associated to.
*/
- private var _sharedValues:
- MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>? =
+ private var _sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>? =
null
- internal val sharedValues:
- MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>
+ internal val sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>
get() =
_sharedValues
- ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>()
- .also { _sharedValues = it }
+ ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>().also {
+ _sharedValues = it
+ }
// TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
private val horizontalDraggableHandler: DraggableHandlerImpl
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a5b6d24..44affd9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -457,7 +457,7 @@
*/
internal fun startTransition(
transition: TransitionState.Transition,
- transitionKey: TransitionKey?,
+ transitionKey: TransitionKey? = null,
chain: Boolean = true,
) {
checkThread()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index a4682ff..465a410 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
@@ -192,7 +193,7 @@
)
}
-interface OverscrollScope {
+interface OverscrollScope : Density {
/**
* Return the absolute distance between fromScene and toScene, if available, otherwise
* [DistanceUnspecified].
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index e8854cf..6e8b208 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -32,7 +32,12 @@
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.util.lerp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.TestScenes.SceneD
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
@@ -130,8 +135,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -189,8 +194,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -243,8 +248,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -381,4 +386,61 @@
}
}
}
+
+ @Test
+ fun animatedValueIsUsingLastTransition() = runTest {
+ val state =
+ rule.runOnUiThread { MutableSceneTransitionLayoutStateImpl(SceneA, transitions {}) }
+
+ val foo = ValueKey("foo")
+ val bar = ValueKey("bar")
+ val lastValues = mutableMapOf<ValueKey, MutableMap<SceneKey, Float>>()
+
+ @Composable
+ fun SceneScope.animateFloat(value: Float, key: ValueKey) {
+ val animatedValue = animateSceneFloatAsState(value, key)
+ LaunchedEffect(animatedValue) {
+ snapshotFlow { animatedValue.value }
+ .collect { lastValues.getOrPut(key) { mutableMapOf() }[sceneKey] = it }
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ // foo goes from 0f to 100f in A => B.
+ scene(SceneA) { animateFloat(0f, foo) }
+ scene(SceneB) { animateFloat(100f, foo) }
+
+ // bar goes from 0f to 10f in C => D.
+ scene(SceneC) { animateFloat(0f, bar) }
+ scene(SceneD) { animateFloat(10f, bar) }
+ }
+ }
+
+ rule.runOnUiThread {
+ // A => B is at 30%.
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.3f },
+ onFinish = neverFinish(),
+ )
+ )
+
+ // C => D is at 70%.
+ state.startTransition(transition(from = SceneC, to = SceneD, progress = { 0.7f }))
+ }
+ rule.waitForIdle()
+
+ assertThat(lastValues[foo]?.get(SceneA)).isWithin(0.001f).of(30f)
+ assertThat(lastValues[foo]?.get(SceneB)).isWithin(0.001f).of(30f)
+ assertThat(lastValues[foo]?.get(SceneC)).isNull()
+ assertThat(lastValues[foo]?.get(SceneD)).isNull()
+
+ assertThat(lastValues[bar]?.get(SceneA)).isNull()
+ assertThat(lastValues[bar]?.get(SceneB)).isNull()
+ assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f)
+ assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 9692fae..beb74bc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -59,6 +59,7 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.util.lerp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
@@ -348,7 +349,7 @@
),
onLayoutImpl = { nullableLayoutImpl = it },
) {
- scene(SceneA) { /* Nothing */}
+ scene(SceneA) { /* Nothing */ }
scene(SceneB) { Box(Modifier.element(key)) }
scene(SceneC) {
when (sceneCState) {
@@ -1083,10 +1084,17 @@
}
val layoutSize = DpSize(200.dp, 100.dp)
+ val lastValues = mutableMapOf<SceneKey, Float>()
@Composable
- fun SceneScope.Foo(size: Dp, modifier: Modifier = Modifier) {
- Box(modifier.element(TestElements.Foo).size(size))
+ fun SceneScope.Foo(size: Dp, value: Float, modifier: Modifier = Modifier) {
+ val sceneKey = this.sceneKey
+ Element(TestElements.Foo, modifier.size(size)) {
+ val animatedValue = animateElementFloatAsState(value, TestValues.Value1)
+ LaunchedEffect(animatedValue) {
+ snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+ }
+ }
}
// The size of Foo when idle in A, B or C.
@@ -1094,6 +1102,11 @@
val sizeInB = 30.dp
val sizeInC = 50.dp
+ // The target value when idle in A, B, or C.
+ val valueInA = 0f
+ val valueInB = 100f
+ val valueInC = 200f
+
lateinit var layoutImpl: SceneTransitionLayoutImpl
rule.setContent {
SceneTransitionLayoutForTesting(
@@ -1103,7 +1116,9 @@
) {
// In scene A, Foo is aligned at the TopStart.
scene(SceneA) {
- Box(Modifier.fillMaxSize()) { Foo(sizeInA, Modifier.align(Alignment.TopStart)) }
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+ }
}
// In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming
@@ -1111,14 +1126,16 @@
// values and deltas are properly cleared once all transitions are done.
scene(SceneC) {
Box(Modifier.fillMaxSize()) {
- Foo(sizeInC, Modifier.align(Alignment.BottomEnd))
+ Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
}
}
// In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming
// from A.
scene(SceneB) {
- Box(Modifier.fillMaxSize()) { Foo(sizeInB, Modifier.align(Alignment.TopEnd)) }
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+ }
}
}
}
@@ -1134,6 +1151,10 @@
.assertSizeIsEqualTo(sizeInA)
.assertPositionInRootIsEqualTo(offsetInA.x, offsetInA.y)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInA)
+ assertThat(lastValues[SceneB]).isNull()
+ assertThat(lastValues[SceneC]).isNull()
+
// Current transition is A => B at 50%.
val aToBProgress = 0.5f
val aToB =
@@ -1145,12 +1166,17 @@
)
val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
+ val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertSizeIsEqualTo(sizeInAToB)
.assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneC]).isNull()
+
// Start B => C at 0%.
var bToCProgress by mutableFloatStateOf(0f)
var interruptionProgress by mutableFloatStateOf(1f)
@@ -1167,6 +1193,11 @@
// to the current transition offset and size.
val offsetInterruptionDelta = offsetInAToB - offsetInB
val sizeInterruptionDelta = sizeInAToB - sizeInB
+ val valueInterruptionDelta = valueInAToB - valueInB
+
+ assertThat(offsetInterruptionDelta).isNotEqualTo(DpOffset.Zero)
+ assertThat(sizeInterruptionDelta).isNotEqualTo(0.dp)
+ assertThat(valueInterruptionDelta).isNotEqualTo(0f)
// Interruption progress is at 100% and bToC is at 0%, so Foo should be at the same offset
// and size as right before the interruption.
@@ -1175,11 +1206,16 @@
.assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
.assertSizeIsEqualTo(sizeInAToB)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInAToB)
+
// Move the transition forward at 30% and set the interruption progress to 50%.
bToCProgress = 0.3f
interruptionProgress = 0.5f
val offsetInBToC = lerp(offsetInB, offsetInC, bToCProgress)
val sizeInBToC = lerp(sizeInB, sizeInC, bToCProgress)
+ val valueInBToC = lerp(valueInB, valueInC, bToCProgress)
val offsetInBToCWithInterruption =
offsetInBToC +
DpOffset(
@@ -1187,6 +1223,9 @@
offsetInterruptionDelta.y * interruptionProgress,
)
val sizeInBToCWithInterruption = sizeInBToC + sizeInterruptionDelta * interruptionProgress
+ val valueInBToCWithInterruption =
+ valueInBToC + valueInterruptionDelta * interruptionProgress
+
rule.waitForIdle()
rule
.onNode(isElement(TestElements.Foo, SceneB))
@@ -1196,6 +1235,10 @@
)
.assertSizeIsEqualTo(sizeInBToCWithInterruption)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInBToCWithInterruption)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInBToCWithInterruption)
+ assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInBToCWithInterruption)
+
// Finish the transition and interruption.
bToCProgress = 1f
interruptionProgress = 0f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 3751a22..08532bd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -461,4 +461,30 @@
assertThat(exception).hasMessageThat().contains(Back.toString())
assertThat(exception).hasMessageThat().contains(SceneA.debugName)
}
+
+ @Test
+ fun sceneKeyInScope() {
+ val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+
+ var keyInA: SceneKey? = null
+ var keyInB: SceneKey? = null
+ var keyInC: SceneKey? = null
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { keyInA = sceneKey }
+ scene(SceneB) { keyInB = sceneKey }
+ scene(SceneC) { keyInC = sceneKey }
+ }
+ }
+
+ // Snap to B then C to compose these scenes at least once.
+ rule.runOnUiThread { state.snapToScene(SceneB) }
+ rule.waitForIdle()
+ rule.runOnUiThread { state.snapToScene(SceneC) }
+ rule.waitForIdle()
+
+ assertThat(keyInA).isEqualTo(SceneA)
+ assertThat(keyInB).isEqualTo(SceneB)
+ assertThat(keyInC).isEqualTo(SceneC)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 3a806a4..25ea2ee 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
@@ -594,4 +595,43 @@
assertThat(transition).hasToScene(SceneB)
assertThat(transition).hasProgress(0.5f, tolerance = 0.01f)
}
+
+ @Test
+ fun overscrollScopeExtendsDensity() {
+ val swipeDistance = 100.dp
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ SceneA,
+ transitions {
+ from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
+
+ overscroll(SceneB, Orientation.Vertical) {
+ translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
+ }
+ }
+ )
+ }
+ val layoutSize = 200.dp
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Box(Modifier.fillMaxSize())
+ }
+ scene(SceneB) { Box(Modifier.element(TestElements.Foo).fillMaxSize()) }
+ }
+ }
+
+ // Swipe down by twice the swipe distance so that we are at 100% overscrolling on scene B.
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(0f, touchSlop + (swipeDistance * 2).toPx()), delayMillis = 1_000)
+ }
+
+ // Foo should be translated by (20dp, 30dp).
+ rule.onNode(isElement(TestElements.Foo)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index a609be4..e6fa69d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -41,8 +41,10 @@
return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
+
override val progress: Float
get() = progress()
+
override val progressVelocity: Float
get() = progressVelocity()
@@ -53,6 +55,8 @@
override val orientation: Orientation = orientation
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
+ override val density: Float = 1f
+ override val fontScale: Float = 1f
override val absoluteDistance = 0f
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 89c5495..fb2b33d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -34,6 +34,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -43,6 +45,7 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -216,6 +219,32 @@
)
}
+ @Test
+ fun backgroundType_defaultValue() =
+ testScope.runTest {
+ val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+ assertThat(backgroundType).isEqualTo(CommunalBackgroundType.DEFAULT)
+ }
+
+ @Test
+ fun backgroundType_verifyAllValues() =
+ testScope.runTest {
+ val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+ for (type in CommunalBackgroundType.entries) {
+ kosmos.fakeSettings.putIntForUser(
+ GLANCEABLE_HUB_BACKGROUND_SETTING,
+ type.value,
+ PRIMARY_USER.id
+ )
+ assertWithMessage(
+ "Expected $type when $GLANCEABLE_HUB_BACKGROUND_SETTING is set to" +
+ " ${type.value} but was $backgroundType"
+ )
+ .that(backgroundType)
+ .isEqualTo(type)
+ }
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 5e19a41..9dcea82 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.communalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -54,6 +55,8 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -62,6 +65,8 @@
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.shade.ShadeTestUtil
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -71,7 +76,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
@@ -85,6 +89,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -138,6 +143,8 @@
)
whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
+ kosmos.powerInteractor.setAwakeForTest()
+
underTest =
CommunalViewModel(
testScope,
@@ -146,6 +153,7 @@
kosmos.keyguardInteractor,
kosmos.communalSceneInteractor,
kosmos.communalInteractor,
+ kosmos.communalSettingsInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
mediaHost,
@@ -468,6 +476,229 @@
assertThat(isFocusable).isEqualTo(false)
}
+ @Test
+ fun isCommunalContentFlowFrozen_whenActivityStartedWhileDreaming() =
+ testScope.runTest {
+ val isCommunalContentFlowFrozen by
+ collectLastValue(underTest.isCommunalContentFlowFrozen)
+
+ // 1. When dreaming not dozing
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceTimeBy(60L)
+ // And keyguard is occluded by dream
+ keyguardRepository.setKeyguardOccluded(true)
+
+ // And on hub
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope = testScope,
+ )
+
+ // Then flow is not frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+
+ // 2. When dreaming stopped by the new activity about to show on lock screen
+ keyguardRepository.setDreamingWithOverlay(false)
+ advanceTimeBy(60L)
+
+ // Then flow is frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(true)
+
+ // 3. When transitioned to OCCLUDED and activity shows
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+
+ // Then flow is not frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+ }
+
+ @Test
+ fun isCommunalContentFlowFrozen_whenActivityStartedInHandheldMode() =
+ testScope.runTest {
+ val isCommunalContentFlowFrozen by
+ collectLastValue(underTest.isCommunalContentFlowFrozen)
+
+ // 1. When on keyguard and not occluded
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+
+ // And transitioned to hub
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope = testScope,
+ )
+
+ // Then flow is not frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+
+ // 2. When occluded by a new activity
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ // And transitioning to occluded
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+
+ // Then flow is frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(true)
+
+ // 3. When transition is finished
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+
+ // Then flow is not frozen
+ assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+ }
+
+ @Test
+ fun communalContent_emitsFrozenContent_whenFrozen() =
+ testScope.runTest {
+ val communalContent by collectLastValue(underTest.communalContent)
+ tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+ // When dreaming
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceTimeBy(60L)
+ keyguardRepository.setKeyguardOccluded(true)
+
+ // And transitioned to hub
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope = testScope,
+ )
+
+ // Widgets available
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel.Available(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = providerInfo,
+ ),
+ CommunalWidgetContentModel.Available(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = providerInfo,
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Then hub shows widgets and the CTA tile
+ assertThat(communalContent).hasSize(3)
+
+ // When dreaming stopped by another activity which should freeze flow
+ keyguardRepository.setDreamingWithOverlay(false)
+ advanceTimeBy(60L)
+
+ // New timer available
+ val target = Mockito.mock(SmartspaceTarget::class.java)
+ whenever<String?>(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+ smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
+ runCurrent()
+
+ // Still only emits widgets and the CTA tile
+ assertThat(communalContent).hasSize(3)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+ assertThat(communalContent?.get(1))
+ .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+ assertThat(communalContent?.get(2))
+ .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+ }
+
+ @Test
+ fun communalContent_emitsLatestContent_whenNotFrozen() =
+ testScope.runTest {
+ val communalContent by collectLastValue(underTest.communalContent)
+ tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+ // When dreaming
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDreamingWithOverlay(true)
+ advanceTimeBy(60L)
+ keyguardRepository.setKeyguardOccluded(true)
+
+ // Transitioned to Glanceable hub.
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope = testScope,
+ )
+
+ // And widgets available
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel.Available(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = providerInfo,
+ ),
+ CommunalWidgetContentModel.Available(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = providerInfo,
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Then emits widgets and the CTA tile
+ assertThat(communalContent).hasSize(3)
+
+ // When new timer available
+ val target = Mockito.mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+ smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
+ runCurrent()
+
+ // Then emits timer, widgets and the CTA tile
+ assertThat(communalContent).hasSize(4)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.Smartspace::class.java)
+ assertThat(communalContent?.get(1))
+ .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+ assertThat(communalContent?.get(2))
+ .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+ assertThat(communalContent?.get(3))
+ .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+ }
+
private suspend fun setIsMainUser(isMainUser: Boolean) {
whenever(user.isMain).thenReturn(isMainUser)
userRepository.setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index c51413a..4849e66 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.qsTileFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -41,6 +42,7 @@
private val kosmos = testKosmos()
private val vibratorHelper = kosmos.vibratorHelper
+ private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
private val effectDuration = 400
private val lowTickDuration = 12
@@ -61,6 +63,7 @@
vibratorHelper,
kosmos.keyguardInteractor,
)
+ longPressEffect.qsTile = qsTile
}
@Test
@@ -91,8 +94,10 @@
// GIVEN an action down event occurs
longPressEffect.handleActionDown()
- // THEN the effect moves to the TIMEOUT_WAIT state
+ // THEN the effect moves to the TIMEOUT_WAIT state and starts the wait
+ val action by collectLastValue(longPressEffect.actionType)
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+ assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
}
@Test
@@ -107,20 +112,6 @@
}
@Test
- fun onActionUp_whileWaiting_performsClick() =
- testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
- // GIVEN an action is being collected
- val action by collectLastValue(longPressEffect.actionType)
-
- // GIVEN an action up occurs
- longPressEffect.handleActionUp()
-
- // THEN the action to invoke is the click action and the effect does not start
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK)
- assertEffectDidNotStart()
- }
-
- @Test
fun onWaitComplete_whileWaiting_beginsEffect() =
testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
// GIVEN the pressed timeout is complete
@@ -221,8 +212,10 @@
// GIVEN that the animator was cancelled
longPressEffect.handleAnimationCancel()
- // THEN the state goes to the timeout wait
+ // THEN the state goes to the timeout wait and the wait is posted
+ val action by collectLastValue(longPressEffect.actionType)
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+ assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
}
@Test
@@ -238,6 +231,29 @@
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
}
+ @Test
+ fun onTileClick_whileWaiting_withQSTile_clicks() =
+ testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is successful
+ assertThat(couldClick).isTrue()
+ }
+
+ @Test
+ fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
+ testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+ // GIVEN that no QSTile has been set
+ longPressEffect.qsTile = null
+
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is not successful
+ assertThat(couldClick).isFalse()
+ }
+
private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
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 5068f68..78a1167 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
@@ -350,7 +350,7 @@
}
@Test
- fun quickAffordance_doNotSendUpdatesWhileShadeExpandingAndStillHidden() =
+ fun quickAffordance_updateOncePerShadeExpansion() =
testScope.runTest {
val shadeExpansion = MutableStateFlow(0f)
whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
@@ -365,9 +365,7 @@
shadeExpansion.value = i / 10f
}
- assertThat(collectedValue[0])
- .isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
- assertThat(collectedValue.size).isEqualTo(initialSize)
+ assertThat(collectedValue.size).isEqualTo(initialSize + 1)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 02993b8..523a89a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -99,14 +99,14 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
@@ -162,6 +162,7 @@
private NotificationEntry mCurrentUserNotif;
private NotificationEntry mSecondaryUserNotif;
private NotificationEntry mWorkProfileNotif;
+ private NotificationEntry mSensitiveContentNotif;
private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
@@ -224,6 +225,14 @@
mWorkProfileNotif.setRanking(new RankingBuilder(mWorkProfileNotif.getRanking())
.setChannel(channel)
.setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ mSensitiveContentNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mCurrentUser.id))
+ .build();
+ mSensitiveContentNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+ .setChannel(channel)
+ .setSensitiveContent(true)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif);
mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
@@ -459,6 +468,17 @@
}
@Test
+ public void testHasSensitiveContent_redacted() {
+ // Allow private notifications for this user
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // Sensitive Content notifications are always redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mSensitiveContentNotif));
+ }
+
+ @Test
public void testUserSwitchedCallsOnUserSwitching() {
mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
mContext);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 82e2bb7..c35c165 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -41,6 +41,8 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -53,6 +55,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.shade.shadeTestUtil
+import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -794,11 +797,47 @@
@DisableSceneContainer
fun updateBounds_fromKeyguardRoot() =
testScope.runTest {
- val bounds by collectLastValue(underTest.bounds)
+ val startProgress = 0f
+ val startStep = TransitionStep(LOCKSCREEN, AOD, startProgress, TransitionState.STARTED)
+ val boundsChangingProgress = 0.2f
+ val boundsChangingStep =
+ TransitionStep(LOCKSCREEN, AOD, boundsChangingProgress, TransitionState.RUNNING)
+ val boundsInterpolatingProgress = 0.6f
+ val boundsInterpolatingStep =
+ TransitionStep(
+ LOCKSCREEN,
+ AOD,
+ boundsInterpolatingProgress,
+ TransitionState.RUNNING
+ )
+ val finishProgress = 1.0f
+ val finishStep =
+ TransitionStep(LOCKSCREEN, AOD, finishProgress, TransitionState.FINISHED)
+ val bounds by collectLastValue(underTest.bounds)
val top = 123f
val bottom = 456f
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(startStep)
+ runCurrent()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsChangingStep)
+ runCurrent()
keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsInterpolatingStep)
+ runCurrent()
+ val adjustedProgress =
+ (boundsInterpolatingProgress - boundsChangingProgress) /
+ (1 - boundsChangingProgress)
+ val interpolatedTop = interpolate(0f, top, adjustedProgress)
+ val interpolatedBottom = interpolate(0f, bottom, adjustedProgress)
+ assertThat(bounds)
+ .isEqualTo(
+ NotificationContainerBounds(top = interpolatedTop, bottom = interpolatedBottom)
+ )
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+ runCurrent()
assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index b83b93b..10a4eb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -50,6 +50,8 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+private const val builtInDeviceName = "This phone"
+
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -71,6 +73,10 @@
addOverride(R.drawable.ic_media_speaker_device, testIcon)
addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
+
+ addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
}
}
}
@@ -90,7 +96,7 @@
assertThat(device).isInstanceOf(AudioOutputDevice.BuiltIn::class.java)
assertThat(device!!.icon).isEqualTo(testIcon)
- assertThat(device!!.name).isEqualTo("built_in")
+ assertThat(device!!.name).isEqualTo(builtInDeviceName)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
new file mode 100644
index 0000000..8921a23
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.volume.panel.component.mediaoutput.domain.interactor
+
+import android.graphics.drawable.TestStubDrawable
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.data.repository.audioSharingRepository
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.localMediaController
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val builtInDeviceName = "This phone"
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MediaOutputComponentInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: MediaOutputComponentInteractor
+
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+ localMediaRepository.updateCurrentConnectedDevice(
+ TestMediaDevicesFactory.builtInMediaDevice(deviceIcon = testIcon)
+ )
+
+ with(context.orCreateTestableResources) {
+ addOverride(R.drawable.ic_smartphone, testIcon)
+
+ addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
+ }
+
+ underTest = mediaOutputComponentInteractor
+ }
+
+ @Test
+ fun inCall_stateIs_Calling() =
+ with(kosmos) {
+ testScope.runTest {
+ with(audioRepository) {
+ setMode(AudioManager.MODE_IN_CALL)
+ setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
+ }
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ assertThat(model)
+ .isEqualTo(
+ MediaOutputComponentModel.Calling(
+ AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+ false,
+ )
+ )
+ }
+ }
+
+ @Test
+ fun hasSession_stateIs_MediaSession() =
+ with(kosmos) {
+ testScope.runTest {
+ mediaControllerRepository.setActiveSessions(listOf(localMediaController))
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ with(model as MediaOutputComponentModel.MediaSession) {
+ assertThat(session.appLabel).isEqualTo("local_media_controller_label")
+ assertThat(session.packageName).isEqualTo("local.test.pkg")
+ assertThat(session.canAdjustVolume).isTrue()
+ assertThat(device)
+ .isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
+ assertThat(isInAudioSharing).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun noMediaOrCall_stateIs_Idle() =
+ with(kosmos) {
+ testScope.runTest {
+ audioSharingRepository.setInAudioSharing(true)
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ assertThat(model)
+ .isEqualTo(
+ MediaOutputComponentModel.Idle(
+ AudioOutputDevice.BuiltIn("built_in_media", testIcon),
+ true,
+ )
+ )
+ }
+ }
+
+ private companion object {
+ val testIcon = TestStubDrawable()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index d497b4a..86a20dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -31,14 +31,11 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.data.repository.audioSharingRepository
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaControllerRepository
-import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -66,10 +63,7 @@
applicationContext,
testScope.backgroundScope,
mediaOutputActionsInteractor,
- mediaDeviceSessionInteractor,
- audioOutputInteractor,
- audioModeInteractor,
- mediaOutputInteractor,
+ mediaOutputComponentInteractor,
uiEventLogger,
)
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
new file mode 100644
index 0000000..82b222e7
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M360,880q-134,0 -227,-93T40,560h80q0,100 70,170t170,70v80ZM360,740q-75,0 -127.5,-52.5T180,560h80q0,42 29,71t71,29v80ZM400,600q-33,0 -56.5,-23.5T320,520v-320q0,-33 23.5,-56.5T400,120h160q33,0 56.5,23.5T640,200v320q0,33 -23.5,56.5T560,600L400,600ZM400,520h160v-320L400,200v320ZM600,740v-80q42,0 71,-29t29,-71h80q0,75 -52.5,127.5T600,740ZM600,880v-80q100,0 170,-70t70,-170h80q0,134 -93,227T600,880ZM400,520h160,-160Z" />
+</vector>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index 1366226..e8eb53f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -17,6 +17,7 @@
package com.android.systemui.flags
import android.app.Activity
+import android.content.pm.PackageManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -35,6 +36,7 @@
) : FlagListenable {
companion object {
const val RECEIVING_PACKAGE = "com.android.systemui"
+ const val RECEIVING_PACKAGE_WATCH = "com.google.android.apps.wearable.systemui"
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
@@ -62,7 +64,7 @@
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
val intent = Intent(ACTION_GET_FLAGS)
- intent.setPackage(RECEIVING_PACKAGE)
+ intent.setPackage(if (isWatch()) RECEIVING_PACKAGE_WATCH else RECEIVING_PACKAGE)
return CallbackToFutureAdapter.getFuture {
completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> ->
@@ -193,6 +195,10 @@
restartAction?.accept(suppressRestart)
}
+ private fun isWatch(): Boolean {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ }
+
private data class PerFlagListener(val name: String, val listener: FlagListenable.Listener)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 3bf3fb3..b116e29 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -308,7 +308,13 @@
} else {
// Don't listen and clear out the text when the device isn't a phone.
mMainExecutor.execute(() -> callback.updateCarrierInfo(
- new CarrierTextCallbackInfo("", null, false, null)
+ new CarrierTextCallbackInfo(
+ /* carrierText= */ "",
+ /* listOfCarriers= */ null,
+ /* anySimReady= */ false,
+ /* isInSatelliteMode= */ false,
+ /* subscriptionIds= */ null,
+ /* airplaneMode= */ false)
));
}
} else {
@@ -448,10 +454,12 @@
displayText = currentSatelliteText;
}
+ boolean isInSatelliteMode = mSatelliteCarrierText != null;
final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
displayText,
carrierNames,
!allSimsMissing,
+ isInSatelliteMode,
subsIds,
airplaneMode);
mLogger.logCallbackSentFromUpdate(info);
@@ -757,21 +765,35 @@
public final CharSequence carrierText;
public final CharSequence[] listOfCarriers;
public final boolean anySimReady;
+ public final boolean isInSatelliteMode;
public final int[] subscriptionIds;
public boolean airplaneMode;
@VisibleForTesting
- public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
- boolean anySimReady, int[] subscriptionIds) {
- this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
+ public CarrierTextCallbackInfo(
+ CharSequence carrierText,
+ CharSequence[] listOfCarriers,
+ boolean anySimReady,
+ int[] subscriptionIds) {
+ this(carrierText,
+ listOfCarriers,
+ anySimReady,
+ /* isInSatelliteMode= */ false,
+ subscriptionIds,
+ /* airplaneMode= */ false);
}
- @VisibleForTesting
- public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
- boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
+ public CarrierTextCallbackInfo(
+ CharSequence carrierText,
+ CharSequence[] listOfCarriers,
+ boolean anySimReady,
+ boolean isInSatelliteMode,
+ int[] subscriptionIds,
+ boolean airplaneMode) {
this.carrierText = carrierText;
this.listOfCarriers = listOfCarriers;
this.anySimReady = anySimReady;
+ this.isInSatelliteMode = isInSatelliteMode;
this.subscriptionIds = subscriptionIds;
this.airplaneMode = airplaneMode;
}
@@ -782,6 +804,7 @@
+ "carrierText=" + carrierText
+ ", listOfCarriers=" + Arrays.toString(listOfCarriers)
+ ", anySimReady=" + anySimReady
+ + ", isInSatelliteMode=" + isInSatelliteMode
+ ", subscriptionIds=" + Arrays.toString(subscriptionIds)
+ ", airplaneMode=" + airplaneMode
+ '}';
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 47e4b49..f688d4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -308,14 +308,14 @@
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
mBgExecutor.execute(() -> {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
false, /* notifyForDescendants */
mDoubleLineClockObserver,
UserHandle.USER_ALL
);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
false, /* notifyForDescendants */
mShowWeatherObserver,
@@ -372,8 +372,8 @@
setClock(null);
mBgExecutor.execute(() -> {
- mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
- mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
+ mSecureSettings.unregisterContentObserverSync(mDoubleLineClockObserver);
+ mSecureSettings.unregisterContentObserverSync(mShowWeatherObserver);
});
mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index ca24ccb..f2a68a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -319,7 +319,7 @@
}
// Unregister observer before removing view
- mSecureSettings.unregisterContentObserver(mMagnificationCapabilityObserver);
+ mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver);
mWindowManager.removeView(mSettingView);
mIsVisible = false;
if (resetPosition) {
@@ -380,7 +380,7 @@
mWindowManager.addView(mSettingView, mParams);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
mMagnificationCapabilityObserver,
UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index eb840f1..ffb5f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -240,26 +240,26 @@
}
void registerObserversAndCallbacks() {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
/* notifyForDescendants */ false,
mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
}
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_OPACITY),
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
index 91bc0c1..eaf541d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
@@ -29,12 +29,12 @@
import android.widget.TextView
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
-import com.android.systemui.res.R
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener.ControlUnitType
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -46,7 +46,9 @@
import kotlin.math.roundToInt
/** The Dialog that contains a seekbar for changing the font size. */
-class FontScalingDialogDelegate @Inject constructor(
+class FontScalingDialogDelegate
+@Inject
+constructor(
private val context: Context,
private val systemUIDialogFactory: SystemUIDialog.Factory,
private val layoutInflater: LayoutInflater,
@@ -84,9 +86,9 @@
dialog.setTitle(R.string.font_scaling_dialog_title)
dialog.setView(layoutInflater.inflate(R.layout.font_scaling_dialog, null))
dialog.setPositiveButton(
- R.string.quick_settings_done,
- /* onClick = */ null,
- /* dismissOnClick = */ true
+ R.string.quick_settings_done,
+ /* onClick = */ null,
+ /* dismissOnClick = */ true
)
}
@@ -142,7 +144,7 @@
}
)
doneButton.setOnClickListener { dialog.dismiss() }
- systemSettings.registerContentObserver(Settings.System.FONT_SCALE, fontSizeObserver)
+ systemSettings.registerContentObserverSync(Settings.System.FONT_SCALE, fontSizeObserver)
}
/**
@@ -165,7 +167,7 @@
override fun onStop(dialog: SystemUIDialog) {
cancelUpdateFontScaleRunnable?.run()
cancelUpdateFontScaleRunnable = null
- systemSettings.unregisterContentObserver(fontSizeObserver)
+ systemSettings.unregisterContentObserverSync(fontSizeObserver)
}
@MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
index 68c4a10..2970890 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
@@ -75,7 +75,7 @@
) {
fun fetch(): Boolean = getIntForUser(key, if (defaultValue) 1 else 0, userId) > 0
- registerContentObserverForUser(
+ registerContentObserverForUserSync(
key,
false /* notifyForDescendants */,
object : ContentObserver(handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 153b7aa..8993a3b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.Flags.communalHub
import com.android.systemui.Flags.restartDreamOnUnocclude
import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -30,11 +31,11 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import javax.inject.Inject
/**
* A [CoreStartable] responsible for automatically starting the dream when the communal hub is
@@ -78,6 +79,7 @@
if (
finishedState == KeyguardState.GLANCEABLE_HUB &&
!dreaming &&
+ !glanceableHubAllowKeyguardWhenDreaming() &&
dreamManager.canStartDreaming(isAwake)
) {
dreamManager.startDream()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 88cb64c..1c47e50 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -30,6 +30,7 @@
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlagsClassic
@@ -59,6 +60,9 @@
/** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
+
+ /** The type of background to use for the hub. Used to experiment with different backgrounds. */
+ fun getBackground(user: UserInfo): Flow<CommunalBackgroundType>
}
@SysUISingleton
@@ -126,6 +130,21 @@
.emitOnStart()
.map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+ override fun getBackground(user: UserInfo): Flow<CommunalBackgroundType> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_BACKGROUND_SETTING))
+ .emitOnStart()
+ .map {
+ val intType =
+ secureSettings.getIntForUser(
+ GLANCEABLE_HUB_BACKGROUND_SETTING,
+ CommunalBackgroundType.DEFAULT.value,
+ user.id
+ )
+ CommunalBackgroundType.entries.find { type -> type.value == intType }
+ ?: CommunalBackgroundType.DEFAULT
+ }
+
private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
secureSettings
.observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
@@ -141,6 +160,7 @@
companion object {
const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
+ const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
private const val ENABLED_SETTING_DEFAULT = 1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 3e5126a..f043d58 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.communal.data.model.CommunalEnabledState
import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -30,6 +31,7 @@
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -38,6 +40,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -47,6 +50,7 @@
@Inject
constructor(
@Background private val bgScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
@Background private val bgExecutor: Executor,
private val repository: CommunalSettingsRepository,
userInteractor: SelectedUserInteractor,
@@ -78,6 +82,12 @@
initialValue = CommunalWidgetCategories.defaultCategories
)
+ /** The type of background to use for the hub. Used to experiment with different backgrounds */
+ val communalBackground: Flow<CommunalBackgroundType> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.getBackground(user) }
+ .flowOn(bgDispatcher)
+
private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
fun send(profiles: List<UserInfo>) {
trySend(profiles.find { it.isManagedProfile })
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
new file mode 100644
index 0000000..8b816db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.communal.shared.model
+
+/** Models the types of background that can be shown on the hub. */
+enum class CommunalBackgroundType(val value: Int) {
+ DEFAULT(0),
+ STATIC_GRADIENT(1),
+ ANIMATED(2),
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 3d9e861..8cd5603 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -91,6 +91,12 @@
/** A list of all the communal content to be displayed in the communal hub. */
abstract val communalContent: Flow<List<CommunalContentModel>>
+ /**
+ * Whether to freeze the emission of the communalContent flow to prevent recomposition. Defaults
+ * to false, indicating that the flow will emit new update.
+ */
+ open val isCommunalContentFlowFrozen: Flow<Boolean> = flowOf(false)
+
/** Whether in edit mode for the communal hub. */
open val isEditMode = false
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 7f3a2dc..3e00b04 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,8 +21,10 @@
import android.view.accessibility.AccessibilityNodeInfo
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -38,7 +40,9 @@
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
@@ -68,6 +72,7 @@
keyguardInteractor: KeyguardInteractor,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -76,8 +81,11 @@
private val logger = Logger(logBuffer, "CommunalViewModel")
+ /** Communal content saved from the previous emission when the flow is active (not "frozen"). */
+ private var frozenCommunalContent: List<CommunalContentModel>? = null
+
@OptIn(ExperimentalCoroutinesApi::class)
- override val communalContent: Flow<List<CommunalContentModel>> =
+ private val latestCommunalContent: Flow<List<CommunalContentModel>> =
tutorialInteractor.isTutorialAvailable
.flatMapLatest { isTutorialMode ->
if (isTutorialMode) {
@@ -93,9 +101,40 @@
}
}
.onEach { models ->
+ frozenCommunalContent = models
logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } }
}
+ /**
+ * Freeze the content flow, when an activity is about to show, like starting a timer via voice:
+ * 1) in handheld mode, use the keyguard occluded state;
+ * 2) in dreaming mode, where keyguard is already occluded by dream, use the dream wakeup
+ * signal. Since in this case the shell transition info does not include
+ * KEYGUARD_VISIBILITY_TRANSIT_FLAGS, KeyguardTransitionHandler will not run the
+ * occludeAnimation on KeyguardViewMediator.
+ */
+ override val isCommunalContentFlowFrozen: Flow<Boolean> =
+ allOf(
+ keyguardTransitionInteractor.isFinishedInState(KeyguardState.GLANCEABLE_HUB),
+ keyguardInteractor.isKeyguardOccluded,
+ not(keyguardInteractor.isAbleToDream)
+ )
+ .distinctUntilChanged()
+ .onEach { logger.d("isCommunalContentFlowFrozen: $it") }
+
+ override val communalContent: Flow<List<CommunalContentModel>> =
+ isCommunalContentFlowFrozen
+ .flatMapLatestConflated { isFrozen ->
+ if (isFrozen) {
+ flowOf(frozenCommunalContent ?: emptyList())
+ } else {
+ latestCommunalContent
+ }
+ }
+ .onEach { models ->
+ logger.d({ "CommunalContent: $str1" }) { str1 = models.joinToString { it.key } }
+ }
+
override val isEmptyState: Flow<Boolean> =
communalInteractor.widgetContent
.map { it.isEmpty() }
@@ -248,6 +287,10 @@
*/
val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
+ /** The type of background to use for the hub. */
+ val communalBackground: Flow<CommunalBackgroundType> =
+ communalSettingsInteractor.communalBackground
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
@@ -255,5 +298,6 @@
sealed class PopupType {
object CtaTile : PopupType()
+
object CustomizeWidgetButton : PopupType()
}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
index 0bdc7f1..84807fb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
@@ -69,15 +69,15 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
settingsObserver,
UserHandle.myUserId());
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
settingsObserver,
UserHandle.myUserId());
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
settingsObserver,
UserHandle.myUserId());
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
index 3cf22b1..a679bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -67,7 +67,7 @@
@Override
public int getRequiredTypeAvailability() {
// TODO(b/339667383): create a new complication type if we decide to productionize this
- return COMPLICATION_TYPE_HOME_CONTROLS;
+ return COMPLICATION_TYPE_NONE;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
index 1bbdfcd..4dbb32d 100644
--- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
@@ -19,18 +19,16 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
/**
- * Retrieves whether or not the device is docked according to DockManager. Emits a starting value of
- * isDocked.
+ * Retrieves whether or not the device is docked according to DockManager. Emits a starting value
+ * of isDocked.
*/
fun DockManager.retrieveIsDocked(): Flow<Boolean> =
ConflatedCallbackFlow.conflatedCallbackFlow {
- val callback = DockManager.DockEventListener { trySend(isDocked) }
- addListener(callback)
- trySend(isDocked)
+ val callback = DockManager.DockEventListener { trySend(isDocked) }
+ addListener(callback)
+ trySend(isDocked)
- awaitClose { removeListener(callback) }
- }
- .distinctUntilChanged()
+ awaitClose { removeListener(callback) }
+ }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 3194942..7ae8409 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -426,7 +426,7 @@
}
if (!anyListening) {
- mSecureSettings.unregisterContentObserver(mSettingsObserver);
+ mSecureSettings.unregisterContentObserverSync(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mTriggerSensors) {
s.registerSettingsObserver(mSettingsObserver);
@@ -750,7 +750,7 @@
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 96e708f..c6c57479 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -18,6 +18,7 @@
import static android.service.dreams.Flags.dreamWakeRedirect;
+import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_MANAGER;
import static com.android.systemui.dreams.dagger.DreamModule.HOME_CONTROL_PANEL_DREAM_COMPONENT;
@@ -395,7 +396,7 @@
return;
}
- redirectWake(mCommunalAvailable);
+ redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index c5b3c53..4b07f78 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.dreams.ui.viewmodel
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -60,7 +61,7 @@
val showGlanceableHub =
communalInteractor.isCommunalEnabled.value &&
!keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
- if (showGlanceableHub) {
+ if (showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()) {
communalInteractor.changeScene(CommunalScenes.Communal)
} else {
toLockscreenTransitionViewModel.startTransition()
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f4f8796..3d3584e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -60,14 +60,6 @@
"notification_drag_to_contents"
)
- /**
- * This flag controls whether we register a listener for StatsD notification memory reports.
- * For statsd to actually call the listener however, a server-side toggle needs to be
- * enabled as well.
- */
- val NOTIFICATION_MEMORY_LOGGING_ENABLED =
- releasedFlag("notification_memory_logging_enabled")
-
// TODO(b/280783617): Tracking Bug
@Keep
@JvmField
@@ -386,9 +378,6 @@
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- // TODO:(b/283203305): Tracking bug
- @JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
-
// TODO(b/298380520): Tracking Bug
@JvmField
val USER_TRACKER_BACKGROUND_CALLBACKS = unreleasedFlag("user_tracker_background_callbacks")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 49be03c..1e4fb4f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -428,7 +428,7 @@
// get notified of phone state changes
mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
mHasVibrator = vibrator.hasVibrator();
@@ -453,7 +453,7 @@
public void destroy() {
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
- mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
+ mGlobalSettings.unregisterContentObserverSync(mAirplaneModeObserver);
mConfigurationController.removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index ea8d7d7..30b9583 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -19,7 +19,9 @@
import android.os.VibrationEffect
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.Expandable
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -51,6 +53,10 @@
var state = State.IDLE
private set
+ /** The QSTile and Expandable used to perform a long-click and click actions */
+ var qsTile: QSTile? = null
+ var expandable: Expandable? = null
+
/** Flow for view control and action */
private val _postedActionType = MutableStateFlow<ActionType?>(null)
val actionType: Flow<ActionType?> =
@@ -105,6 +111,7 @@
when (state) {
State.IDLE -> {
setState(State.TIMEOUT_WAIT)
+ _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
}
State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR
else -> {}
@@ -112,16 +119,9 @@
}
fun handleActionUp() {
- when (state) {
- State.TIMEOUT_WAIT -> {
- _postedActionType.value = ActionType.CLICK
- setState(State.IDLE)
- }
- State.RUNNING_FORWARD -> {
- _postedActionType.value = ActionType.REVERSE_ANIMATOR
- setState(State.RUNNING_BACKWARDS)
- }
- else -> {}
+ if (state == State.RUNNING_FORWARD) {
+ _postedActionType.value = ActionType.REVERSE_ANIMATOR
+ setState(State.RUNNING_BACKWARDS)
}
}
@@ -129,6 +129,7 @@
when (state) {
State.TIMEOUT_WAIT -> {
setState(State.IDLE)
+ clearActionType()
}
State.RUNNING_FORWARD -> {
_postedActionType.value = ActionType.REVERSE_ANIMATOR
@@ -145,18 +146,23 @@
/** This function is called both when an animator completes or gets cancelled */
fun handleAnimationComplete() {
- if (state == State.RUNNING_FORWARD) {
- vibrate(snapEffect)
- _postedActionType.value = ActionType.LONG_PRESS
- }
- if (state != State.TIMEOUT_WAIT) {
- // This will happen if the animator did not finish by being cancelled
- setState(State.IDLE)
+ when (state) {
+ State.RUNNING_FORWARD -> {
+ setState(State.IDLE)
+ vibrate(snapEffect)
+ _postedActionType.value = ActionType.LONG_PRESS
+ }
+ State.RUNNING_BACKWARDS -> {
+ setState(State.IDLE)
+ clearActionType()
+ }
+ else -> {}
}
}
fun handleAnimationCancel() {
setState(State.TIMEOUT_WAIT)
+ _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
}
fun handleTimeoutComplete() {
@@ -190,9 +196,22 @@
effectDuration
)
setState(State.IDLE)
+ clearActionType()
return true
}
+ fun onTileClick(): Boolean {
+ if (state == State.TIMEOUT_WAIT) {
+ setState(State.IDLE)
+ clearActionType()
+ qsTile?.let {
+ it.click(expandable)
+ return true
+ }
+ }
+ return false
+ }
+
enum class State {
IDLE, /* The effect is idle waiting for touch input */
TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */
@@ -202,7 +221,7 @@
/* A type of action to perform on the view depending on the effect's state and logic */
enum class ActionType {
- CLICK,
+ WAIT_TAP_TIMEOUT,
LONG_PRESS,
RESET_AND_LONG_PRESS,
START_ANIMATOR,
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
index 4875f48..92a55ef 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
@@ -17,8 +17,6 @@
package com.android.systemui.haptics.qs
import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.animation.doOnCancel
@@ -30,6 +28,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.qs.tileimpl.QSTileViewImpl
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
object QSLongPressEffectViewBinder {
@@ -41,9 +40,6 @@
): DisposableHandle? {
if (qsLongPressEffect == null) return null
- // Set the touch listener as the long-press effect
- setTouchListener(tile, qsLongPressEffect)
-
return tile.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
// Action to perform
@@ -52,18 +48,18 @@
qsLongPressEffect.actionType.filterNotNull().collect { action ->
when (action) {
- QSLongPressEffect.ActionType.CLICK -> {
- tile.performClick()
- qsLongPressEffect.clearActionType()
+ QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT -> {
+ delay(ViewConfiguration.getTapTimeout().toLong())
+ qsLongPressEffect.handleTimeoutComplete()
}
QSLongPressEffect.ActionType.LONG_PRESS -> {
tile.prepareForLaunch()
- tile.performLongClick()
+ qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
qsLongPressEffect.clearActionType()
}
QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
tile.resetLongPressEffectProperties()
- tile.performLongClick()
+ qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
qsLongPressEffect.clearActionType()
}
QSLongPressEffect.ActionType.START_ANIMATOR -> {
@@ -106,22 +102,4 @@
}
}
}
-
- @SuppressLint("ClickableViewAccessibility")
- private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) {
- tile.setOnTouchListener { _, event ->
- when (event.actionMasked) {
- MotionEvent.ACTION_DOWN -> {
- tile.postDelayed(
- { longPressEffect?.handleTimeoutComplete() },
- ViewConfiguration.getTapTimeout().toLong(),
- )
- longPressEffect?.handleActionDown()
- }
- MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp()
- MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel()
- }
- true
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 81c2d92..f3a1843 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2380,7 +2380,10 @@
}
mCustomMessage = message;
mKeyguardViewControllerLazy.get().dismissAndCollapse();
- } else if (callback != null) {
+ return;
+ }
+ Log.w(TAG, "Ignoring request to DISMISS because mShowing=false");
+ if (callback != null) {
new DismissCallbackWrapper(callback).notifyDismissError();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index 3cbcb2c..97ea16d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -18,21 +18,15 @@
import android.annotation.WorkerThread
import android.content.ComponentCallbacks2
-import android.graphics.HardwareRenderer
-import android.os.Trace
import android.util.Log
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
@@ -40,10 +34,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
/**
@@ -57,37 +48,15 @@
class ResourceTrimmer
@Inject
constructor(
- private val keyguardInteractor: KeyguardInteractor,
- private val powerInteractor: PowerInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val globalWindowManager: GlobalWindowManager,
@Application private val applicationScope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
- private val featureFlags: FeatureFlags,
private val sceneInteractor: SceneInteractor,
-) : CoreStartable, WakefulnessLifecycle.Observer {
+) : CoreStartable {
override fun start() {
Log.d(LOG_TAG, "Resource trimmer registered.")
- if (com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
- applicationScope.launch(bgDispatcher) {
- // We need to wait for the AoD transition (and animation) to complete.
- // This means we're waiting for isDreaming (== implies isDoze) and dozeAmount == 1f
- // signal. This is to make sure we don't clear font caches during animation which
- // would jank and leave stale data in memory.
- val isDozingFully =
- keyguardInteractor.dozeAmount.map { it == 1f }.distinctUntilChanged()
- combine(
- powerInteractor.isAsleep,
- keyguardInteractor.isDreaming,
- isDozingFully,
- ::Triple
- )
- .distinctUntilChanged()
- .collect { onWakefulnessUpdated(it.first, it.second, it.third) }
- }
- }
-
applicationScope.launch(bgDispatcher) {
// We drop 1 to avoid triggering on initial collect().
if (SceneContainerFlag.isEnabled) {
@@ -110,47 +79,9 @@
// lockscreen elements, especially clocks.
Log.d(LOG_TAG, "Sending TRIM_MEMORY_UI_HIDDEN.")
globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- if (featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK)) {
- if (DEBUG) {
- Log.d(LOG_TAG, "Trimming font caches since keyguard went away.")
- }
- globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
- }
- }
-
- @WorkerThread
- private fun onWakefulnessUpdated(
- isAsleep: Boolean,
- isDreaming: Boolean,
- isDozingFully: Boolean
- ) {
- if (!com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
- return
- }
-
- if (DEBUG) {
- Log.d(LOG_TAG, "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully")
- }
- // There are three scenarios:
- // * No dozing and no AoD at all - where we go directly to ASLEEP with isDreaming = false
- // and dozeAmount == 0f
- // * Dozing without Aod - where we go to ASLEEP with isDreaming = true and dozeAmount jumps
- // to 1f
- // * AoD - where we go to ASLEEP with iDreaming = true and dozeAmount slowly increases
- // to 1f
- val dozeDisabledAndScreenOff = isAsleep && !isDreaming
- val dozeEnabledAndDozeAnimationCompleted = isAsleep && isDreaming && isDozingFully
- if (dozeDisabledAndScreenOff || dozeEnabledAndDozeAnimationCompleted) {
- Trace.beginSection("ResourceTrimmer#trimMemory")
- Log.d(LOG_TAG, "SysUI asleep, trimming memory.")
- globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
- Trace.endSection()
- }
}
companion object {
private const val LOG_TAG = "ResourceTrimmer"
- private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index f488d3b..8ec460a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -265,7 +265,7 @@
state: TransitionState
) {
if (updateTransitionId != transitionId) {
- Log.w(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+ Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index c44a40f..b142b44 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -47,8 +48,10 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import javax.inject.Provider
@@ -66,7 +69,9 @@
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -96,17 +101,54 @@
// TODO(b/296118689): move to a repository
private val _notificationPlaceholderBounds = MutableStateFlow(NotificationContainerBounds())
+ // When going to AOD, we interpolate bounds when receiving the new bounds
+ // When going back to LS, we'll apply new bounds directly
+ private val _nonSplitShadeNotifciationPlaceholderBounds =
+ _notificationPlaceholderBounds.pairwise().flatMapLatest { (oldBounds, newBounds) ->
+ val lastChangeStep = keyguardTransitionInteractor.transitionState.first()
+ if (lastChangeStep.to == AOD) {
+ keyguardTransitionInteractor.transitionState.map { step ->
+ val startingProgress = lastChangeStep.value
+ val progress = step.value
+ if (step.to == AOD && progress >= startingProgress) {
+ val adjustedProgress =
+ ((progress - startingProgress) / (1F - startingProgress)).coerceIn(
+ 0F,
+ 1F
+ )
+ val top = interpolate(oldBounds.top, newBounds.top, adjustedProgress)
+ val bottom =
+ interpolate(
+ oldBounds.bottom,
+ newBounds.bottom,
+ adjustedProgress.coerceIn(0F, 1F)
+ )
+ NotificationContainerBounds(top = top, bottom = bottom)
+ } else {
+ newBounds
+ }
+ }
+ } else {
+ flow { emit(newBounds) }
+ }
+ }
+
/** Bounds of the notification container. */
val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
SceneContainerFlag.assertInLegacyMode()
combine(
_notificationPlaceholderBounds,
+ _nonSplitShadeNotifciationPlaceholderBounds,
sharedNotificationContainerInteractor.get().configurationBasedDimensions,
- ) { bounds, cfg ->
+ ) { bounds, nonSplitShadeBounds: NotificationContainerBounds, cfg ->
// We offset the placeholder bounds by the configured top margin to account for
// legacy placement behavior within notifications for splitshade.
- if (MigrateClocksToBlueprint.isEnabled && cfg.useSplitShade) {
- bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
+ if (MigrateClocksToBlueprint.isEnabled) {
+ if (cfg.useSplitShade) {
+ bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
+ } else {
+ nonSplitShadeBounds
+ }
} else bounds
}
.stateIn(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 8ffa4bb..ccce3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -105,35 +105,33 @@
}
return combine(
- quickAffordanceAlwaysVisible(position),
- keyguardInteractor.isDozing,
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor
- .get()
- .transitionState
- .map {
- when (it) {
- is ObservableTransitionState.Idle ->
- it.currentScene == Scenes.Lockscreen
- is ObservableTransitionState.Transition ->
- it.fromScene == Scenes.Lockscreen ||
- it.toScene == Scenes.Lockscreen
- }
+ quickAffordanceAlwaysVisible(position),
+ keyguardInteractor.isDozing,
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor
+ .get()
+ .transitionState
+ .map {
+ when (it) {
+ is ObservableTransitionState.Idle ->
+ it.currentScene == Scenes.Lockscreen
+ is ObservableTransitionState.Transition ->
+ it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen
}
- .distinctUntilChanged()
- } else {
- keyguardInteractor.isKeyguardShowing
- },
- shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
- biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
- affordance
- } else {
- KeyguardQuickAffordanceModel.Hidden
- }
+ }
+ .distinctUntilChanged()
+ } else {
+ keyguardInteractor.isKeyguardShowing
+ },
+ shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
+ biometricSettingsRepository.isCurrentUserInLockdown,
+ ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
+ affordance
+ } else {
+ KeyguardQuickAffordanceModel.Hidden
}
- .distinctUntilChanged()
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 6d579f3..f5b82cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -40,6 +40,7 @@
rebuildSections: List<KeyguardSection> = listOf(),
bindData: Boolean = true
) {
+ rebuildSections.forEach { it.onRebuildBegin() }
val prevSections = previousBlueprint?.sections ?: listOf()
val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
@@ -49,6 +50,7 @@
it.bindData(constraintLayout)
}
}
+ rebuildSections.forEach { it.onRebuildEnd() }
}
/** Rebuilds views for the target sections, or all of them if unspecified. */
@@ -61,6 +63,7 @@
return
}
+ rebuildSections.forEach { it.onRebuildBegin() }
rebuildSections.forEach { it.removeViews(constraintLayout) }
rebuildSections.forEach {
it.addViews(constraintLayout)
@@ -68,6 +71,7 @@
it.bindData(constraintLayout)
}
}
+ rebuildSections.forEach { it.onRebuildEnd() }
}
fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
index 48a2146..473b08f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -33,6 +33,12 @@
/** Removes views and does any data binding destruction. */
abstract fun removeViews(constraintLayout: ConstraintLayout)
+ /* Notifies the section is being rebuilt */
+ open fun onRebuildBegin() {}
+
+ /* Notifies the secion that the rebuild is complete */
+ open fun onRebuildEnd() {}
+
/**
* Defines equality as same class.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 807c322..23c2491 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -65,7 +65,7 @@
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
launch("$TAG#viewModel.alpha") {
// Do not independently apply alpha, as [KeyguardRootViewModel] should work
// for this and all its children
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 1cf009d..b9a79dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,6 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
@@ -81,8 +80,8 @@
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.collect") {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
viewModel.collect { buttonModel ->
updateButton(
view = button,
@@ -94,7 +93,7 @@
}
}
- launch("$TAG#updateButtonAlpha") {
+ launch {
updateButtonAlpha(
view = button,
viewModel = viewModel,
@@ -102,7 +101,7 @@
)
}
- launch("$TAG#configurationBasedDimensions") {
+ launch {
configurationBasedDimensions.collect { dimensions ->
button.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
@@ -324,6 +323,4 @@
private data class ConfigurationBasedDimensions(
val buttonSizePx: Size,
)
-
- private const val TAG = "KeyguardQuickAffordanceViewBinder"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 2d6690f..0435531 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -56,6 +56,14 @@
private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
private var pastVisibility: Int = -1
+ override fun onRebuildBegin() {
+ smartspaceController.suppressDisconnects = true
+ }
+
+ override fun onRebuildEnd() {
+ smartspaceController.suppressDisconnects = false
+ }
+
override fun addViews(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 198e9f2..940f423 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -65,7 +65,7 @@
}
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = ClockSize.LARGE,
)
@@ -74,7 +74,7 @@
.map { it == ClockSize.LARGE }
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = true,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 244d842..c4383fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,23 +28,19 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancesCombinedViewModel
@Inject
constructor(
- @Application applicationScope: CoroutineScope,
private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
private val keyguardInteractor: KeyguardInteractor,
shadeInteractor: ShadeInteractor,
@@ -89,20 +84,15 @@
/** The only time the expansion is important is while lockscreen is actively displayed */
private val shadeExpansionAlpha =
combine(
- showingLockscreen,
- shadeInteractor.anyExpansion,
- ) { showingLockscreen, expansion ->
- if (showingLockscreen) {
- 1 - expansion
- } else {
- 0f
- }
+ showingLockscreen,
+ shadeInteractor.anyExpansion,
+ ) { showingLockscreen, expansion ->
+ if (showingLockscreen) {
+ 1 - expansion
+ } else {
+ 0f
}
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Lazily,
- initialValue = 0f,
- )
+ }
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 8316b3a..edead51 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -42,6 +42,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -123,6 +124,7 @@
class MediaCarouselController
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
private val context: Context,
private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityProvider: VisualStabilityProvider,
@@ -387,15 +389,15 @@
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForAnyStateToGoneKeyguardTransition(this)
listenForAnyStateToLockscreenTransition(this)
- listenForLockscreenSettingChanges(this)
if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
listenForMediaItemsChanges(this)
}
}
+ listenForLockscreenSettingChanges(applicationScope)
// Notifies all active players about animation scale changes.
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
animationScaleObserver
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 6589038..601d563 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -167,6 +167,7 @@
private var targetBounds: Rect = Rect()
private val mediaFrame
get() = mediaCarouselController.mediaFrame
+
private var statusbarState: Int = statusBarStateController.state
private var animator =
ValueAnimator.ofFloat(0.0f, 1.0f).apply {
@@ -600,7 +601,7 @@
}
}
}
- secureSettings.registerContentObserverForUser(
+ secureSettings.registerContentObserverForUserSync(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
settingsObserver,
UserHandle.USER_ALL
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 2dc09e5..41cd2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -500,11 +500,13 @@
final boolean previousForcedVisible = mIsButtonForcedVisible;
mIsButtonForcedVisible =
mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+ // Update this before calling mButtonForcedVisibleCallback since NavigationBar will relayout
+ // and query isHandlingGestures() as a part of the callback
+ mIsBackGestureAllowed = !mIsButtonForcedVisible;
if (previousForcedVisible != mIsButtonForcedVisible
&& mButtonForcedVisibleCallback != null) {
mButtonForcedVisibleCallback.accept(mIsButtonForcedVisible);
}
- mIsBackGestureAllowed = !mIsButtonForcedVisible;
final DisplayMetrics dm = res.getDisplayMetrics();
final float defaultGestureHeight = res.getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index 2d460a0..765b45b 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -341,7 +341,7 @@
}
mQRCodeScannerPreferenceObserver.forEach((key, value) -> {
- mSecureSettings.unregisterContentObserver(value);
+ mSecureSettings.unregisterContentObserverSync(value);
});
// Reset cached values to default as we are no longer listening
@@ -418,7 +418,7 @@
});
}
});
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(LOCK_SCREEN_SHOW_QR_CODE_SCANNER), false,
mQRCodeScannerPreferenceObserver.get(userId), userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index 2fafba1..e4bafcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -52,7 +52,9 @@
*
* It also handles restore gracefully.
*/
-class AutoAddTracker @VisibleForTesting constructor(
+class AutoAddTracker
+@VisibleForTesting
+constructor(
private val secureSettings: SecureSettings,
private val broadcastDispatcher: BroadcastDispatcher,
private val qsHost: QSHost,
@@ -66,39 +68,43 @@
private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
}
- @GuardedBy("autoAdded")
- private val autoAdded = ArraySet<String>()
+ @GuardedBy("autoAdded") private val autoAdded = ArraySet<String>()
private var restoredTiles: Map<String, AutoTile>? = null
override val currentUserId: Int
get() = userId
- private val contentObserver = object : ContentObserver(mainHandler) {
- override fun onChange(
- selfChange: Boolean,
- uris: Collection<Uri>,
- flags: Int,
- _userId: Int
- ) {
- if (_userId != userId) {
- // Ignore changes outside of our user. We'll load the correct value on user change
- return
+ private val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: Collection<Uri>,
+ flags: Int,
+ _userId: Int
+ ) {
+ if (_userId != userId) {
+ // Ignore changes outside of our user. We'll load the correct value on user
+ // change
+ return
+ }
+ loadTiles()
}
- loadTiles()
}
- }
- private val restoreReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action != Intent.ACTION_SETTING_RESTORED) return
- processRestoreIntent(intent)
+ private val restoreReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != Intent.ACTION_SETTING_RESTORED) return
+ processRestoreIntent(intent)
+ }
}
- }
private fun processRestoreIntent(intent: Intent) {
when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
Settings.Secure.QS_TILES -> {
- restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+ restoredTiles =
+ intent
+ .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
?.split(DELIMITER)
?.mapIndexed(::AutoTile)
?.associateBy(AutoTile::tileType)
@@ -109,13 +115,11 @@
}
Settings.Secure.QS_AUTO_ADDED_TILES -> {
restoredTiles?.let { restoredTiles ->
- val restoredAutoAdded = intent
- .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(DELIMITER)
+ val restoredAutoAdded =
+ intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)?.split(DELIMITER)
?: emptyList()
- val autoAddedBeforeRestore = intent
- .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
- ?.split(DELIMITER)
+ val autoAddedBeforeRestore =
+ intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)?.split(DELIMITER)
?: emptyList()
val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
@@ -123,50 +127,51 @@
Log.d(TAG, "Removing tiles: $tilesToRemove")
qsHost.removeTiles(tilesToRemove)
}
- val tiles = synchronized(autoAdded) {
- autoAdded.clear()
- autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
- getTilesFromListLocked()
- }
+ val tiles =
+ synchronized(autoAdded) {
+ autoAdded.clear()
+ autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
+ getTilesFromListLocked()
+ }
saveTiles(tiles)
- } ?: run {
- Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
- "${Settings.Secure.QS_TILES} for user $userId")
}
+ ?: run {
+ Log.w(
+ TAG,
+ "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
+ "${Settings.Secure.QS_TILES} for user $userId"
+ )
+ }
}
else -> {} // Do nothing for other Settings
}
}
- /**
- * Init method must be called after construction to start listening
- */
+ /** Init method must be called after construction to start listening */
fun initialize() {
dumpManager.registerDumpable(TAG, this)
loadTiles()
- secureSettings.registerContentObserverForUser(
- secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
- contentObserver,
- UserHandle.USER_ALL
+ secureSettings.registerContentObserverForUserSync(
+ secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
+ contentObserver,
+ UserHandle.USER_ALL
)
registerBroadcastReceiver()
}
- /**
- * Unregister listeners, receivers and observers
- */
+ /** Unregister listeners, receivers and observers */
fun destroy() {
dumpManager.unregisterDumpable(TAG)
- secureSettings.unregisterContentObserver(contentObserver)
+ secureSettings.unregisterContentObserverSync(contentObserver)
unregisterBroadcastReceiver()
}
private fun registerBroadcastReceiver() {
broadcastDispatcher.registerReceiver(
- restoreReceiver,
- FILTER,
- backgroundExecutor,
- UserHandle.of(userId)
+ restoreReceiver,
+ FILTER,
+ backgroundExecutor,
+ UserHandle.of(userId)
)
}
@@ -186,13 +191,9 @@
fun getRestoredTilePosition(tile: String): Int =
restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
- /**
- * Returns `true` if the tile has been auto-added before
- */
+ /** Returns `true` if the tile has been auto-added before */
fun isAdded(tile: String): Boolean {
- return synchronized(autoAdded) {
- tile in autoAdded
- }
+ return synchronized(autoAdded) { tile in autoAdded }
}
/**
@@ -201,13 +202,14 @@
* From here on, [isAdded] will return true for that tile.
*/
fun setTileAdded(tile: String) {
- val tiles = synchronized(autoAdded) {
- if (autoAdded.add(tile)) {
- getTilesFromListLocked()
- } else {
- null
+ val tiles =
+ synchronized(autoAdded) {
+ if (autoAdded.add(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
}
- }
tiles?.let { saveTiles(it) }
}
@@ -217,13 +219,14 @@
* This allows for this tile to be auto-added again in the future.
*/
fun setTileRemoved(tile: String) {
- val tiles = synchronized(autoAdded) {
- if (autoAdded.remove(tile)) {
- getTilesFromListLocked()
- } else {
- null
+ val tiles =
+ synchronized(autoAdded) {
+ if (autoAdded.remove(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
}
- }
tiles?.let { saveTiles(it) }
}
@@ -233,12 +236,12 @@
private fun saveTiles(tiles: String) {
secureSettings.putStringForUser(
- Settings.Secure.QS_AUTO_ADDED_TILES,
- tiles,
- /* tag */ null,
- /* makeDefault */ false,
- userId,
- /* overrideableByRestore */ true
+ Settings.Secure.QS_AUTO_ADDED_TILES,
+ tiles,
+ /* tag */ null,
+ /* makeDefault */ false,
+ userId,
+ /* overrideableByRestore */ true
)
}
@@ -261,7 +264,9 @@
}
@SysUISingleton
- class Builder @Inject constructor(
+ class Builder
+ @Inject
+ constructor(
private val secureSettings: SecureSettings,
private val broadcastDispatcher: BroadcastDispatcher,
private val qsHost: QSHost,
@@ -278,16 +283,16 @@
fun build(): AutoAddTracker {
return AutoAddTracker(
- secureSettings,
- broadcastDispatcher,
- qsHost,
- dumpManager,
- handler,
- executor,
- userId
+ secureSettings,
+ broadcastDispatcher,
+ qsHost,
+ dumpManager,
+ handler,
+ executor,
+ userId
)
}
}
private data class AutoTile(val index: Int, val tileType: String)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
index 4fc6609..846d63f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
@@ -77,8 +77,8 @@
public void onUserChanged(int newUser, Context userContext) {
synchronized (mListeners) {
if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, newUser);
}
@@ -94,7 +94,7 @@
if (!mListeners.contains(listener)) {
mListeners.add(listener);
if (mListeners.size() == 1) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, mUserTracker.getUserId());
}
@@ -106,7 +106,7 @@
public void removeCallback(@androidx.annotation.NonNull Listener listener) {
synchronized (mListeners) {
if (mListeners.remove(listener) && mListeners.size() == 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
index eb11568..6092348 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
@@ -74,10 +74,10 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserver(
+ mSettingsProxy.registerContentObserverSync(
mSettingsProxy.getUriFor(mSettingName), false, this);
} else {
- mSettingsProxy.unregisterContentObserver(this);
+ mSettingsProxy.unregisterContentObserverSync(this);
mObservedValue = mDefaultValue;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
index 539c2d6..1b34c33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
@@ -76,10 +76,10 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserverForUser(
+ mSettingsProxy.registerContentObserverForUserSync(
mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
} else {
- mSettingsProxy.unregisterContentObserver(this);
+ mSettingsProxy.unregisterContentObserverSync(this);
mObservedValue = mDefaultValue;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
index d452241..9fcb0db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -44,9 +44,8 @@
@Background private val bgDispatcher: CoroutineDispatcher,
) {
- private val changeEvents = MutableSharedFlow<ChangeAction>(
- extraBufferCapacity = CHANGES_BUFFER_SIZE
- )
+ private val changeEvents =
+ MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
@@ -85,8 +84,8 @@
trySend(Unit)
}
}
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
- awaitClose { secureSettings.unregisterContentObserver(observer) }
+ secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserverSync(observer) }
}
.map { load() }
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index aca8733..1f9570a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -98,8 +98,8 @@
trySend(Unit)
}
}
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
- awaitClose { secureSettings.unregisterContentObserver(observer) }
+ secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserverSync(observer) }
}
.map { loadTilesFromSettings(userId) }
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 1143c30..762dacd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -19,6 +19,7 @@
import android.animation.ArgbEvaluator
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
@@ -37,6 +38,7 @@
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
@@ -395,15 +397,22 @@
override fun init(tile: QSTile) {
val expandable = Expandable.fromView(this)
- init(
+ if (quickSettingsVisualHapticsLongpress()) {
+ isHapticFeedbackEnabled = false
+ longPressEffect?.qsTile = tile
+ longPressEffect?.expandable = expandable
+ init(
+ { _: View? -> longPressEffect?.onTileClick() },
+ null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
+ )
+ } else {
+ init(
{ _: View? -> tile.click(expandable) },
{ _: View? ->
tile.longClick(expandable)
true
- }
- )
- if (quickSettingsVisualHapticsLongpress()) {
- isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect]
+ },
+ )
}
}
@@ -541,6 +550,20 @@
return sb.toString()
}
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ // let the View run the onTouch logic for click and long-click detection
+ val result = super.onTouchEvent(event)
+ if (longPressEffect != null) {
+ when (event?.actionMasked) {
+ MotionEvent.ACTION_DOWN -> longPressEffect.handleActionDown()
+ MotionEvent.ACTION_UP -> longPressEffect.handleActionUp()
+ MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel()
+ }
+ }
+ return result
+ }
+
// HANDLE STATE CHANGES RELATED METHODS
protected open fun handleStateChanged(state: QSTile.State) {
@@ -675,7 +698,6 @@
// Long-press effects might have been enabled before but the new state does not
// handle a long-press. In this case, we go back to the behaviour of a regular tile
// and clean-up the resources
- setOnTouchListener(null)
unbindLongPressEffect()
showRippleEffect = isClickable
initialLongPressProperties = null
diff --git a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
index 3c0aa38..09fd7df 100644
--- a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
@@ -66,9 +66,9 @@
}
}
- globalSettings.registerContentObserver(RETAIL_MODE_SETTING, observer)
+ globalSettings.registerContentObserverSync(RETAIL_MODE_SETTING, observer)
- awaitClose { globalSettings.unregisterContentObserver(observer) }
+ awaitClose { globalSettings.unregisterContentObserverSync(observer) }
}
.onStart { emit(Unit) }
.map { globalSettings.getInt(RETAIL_MODE_SETTING, 0) != 0 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 37f2a21..49810762 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -137,14 +137,14 @@
public void startObserving() {
if (!mObserving) {
mObserving = true;
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
BRIGHTNESS_MODE_URI,
false, this, UserHandle.USER_ALL);
}
}
public void stopObserving() {
- mSecureSettings.unregisterContentObserver(this);
+ mSecureSettings.unregisterContentObserverSync(this);
mObserving = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 5e4b173..a171d33 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -381,7 +381,10 @@
mLogger.logHandleUpdateCarrierInfo(info);
mNoSimTextView.setVisibility(View.GONE);
- if (!info.airplaneMode && info.anySimReady) {
+ if (info.isInSatelliteMode) {
+ mLogger.logUsingSatelliteText(info.carrierText);
+ showSingleText(info.carrierText);
+ } else if (!info.airplaneMode && info.anySimReady) {
boolean[] slotSeen = new boolean[SIM_SLOTS];
if (info.listOfCarriers.length == info.subscriptionIds.length) {
mLogger.logUsingSimViews();
@@ -416,22 +419,31 @@
info.listOfCarriers.length, info.subscriptionIds.length);
}
} else {
+ // No sims or airplane mode (but not WFC), so just show the main carrier text.
mLogger.logUsingNoSimView(info.carrierText);
- // No sims or airplane mode (but not WFC). Do not show ShadeCarrierGroup,
- // instead just show info.carrierText in a different view.
- for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i] = mInfos[i].changeVisibility(false);
- mCarrierGroups[i].setCarrierText("");
- mCarrierGroups[i].setVisibility(View.GONE);
- }
- mNoSimTextView.setText(info.carrierText);
- if (!TextUtils.isEmpty(info.carrierText)) {
- mNoSimTextView.setVisibility(View.VISIBLE);
- }
+ showSingleText(info.carrierText);
}
handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
}
+ /**
+ * Shows only the given text in a single TextView and hides ShadeCarrierGroup (which would show
+ * individual SIM details).
+ */
+ private void showSingleText(CharSequence text) {
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mInfos[i] = mInfos[i].changeVisibility(false);
+ mCarrierGroups[i].setCarrierText("");
+ mCarrierGroups[i].setVisibility(View.GONE);
+ }
+ // TODO(b/341841138): Re-name this view now that it's being used for more than just the
+ // no-SIM case.
+ mNoSimTextView.setText(text);
+ if (!TextUtils.isEmpty(text)) {
+ mNoSimTextView.setVisibility(View.VISIBLE);
+ }
+ }
+
private static class H extends Handler {
private Consumer<CarrierTextManager.CarrierTextCallbackInfo> mUpdateCarrierInfo;
private Runnable mUpdateState;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
index af06a35..b563cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
@@ -65,6 +65,15 @@
)
}
+ fun logUsingSatelliteText(text: CharSequence) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ { str1 = "$text" },
+ { "â”— updating No SIM view with satellite text=$str1" },
+ )
+ }
+
fun logUsingSimViews() {
buffer.log(TAG, LogLevel.VERBOSE, {}, { "â”— updating SIM views" })
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 9885fe4..e7fc18e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade.domain.interactor
import com.android.keyguard.LockIconViewController
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -30,7 +31,8 @@
class ShadeLockscreenInteractorImpl
@Inject
constructor(
- @Background private val scope: CoroutineScope,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundScope: CoroutineScope,
private val shadeInteractor: ShadeInteractor,
private val sceneInteractor: SceneInteractor,
private val lockIconViewController: LockIconViewController,
@@ -68,7 +70,7 @@
// Now handled elsewhere. Do nothing.
}
override fun transitionToExpandedShade(delay: Long) {
- scope.launch {
+ backgroundScope.launch {
delay(delay)
changeToShadeScene()
}
@@ -96,10 +98,12 @@
}
private fun changeToShadeScene() {
- val shadeMode = shadeInteractor.shadeMode.value
- sceneInteractor.changeScene(
- if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
- "ShadeLockscreenInteractorImpl.expandToNotifications",
- )
+ applicationScope.launch {
+ val shadeMode = shadeInteractor.shadeMode.value
+ sceneInteractor.changeScene(
+ if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
+ "ShadeLockscreenInteractorImpl.expandToNotifications",
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 95768e5..2dfc920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -318,10 +318,10 @@
};
// Register to listen for changes in Settings.Secure settings.
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, mContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
UserHandle.USER_CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 854ef92..bb26f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
import static android.app.Flags.keyguardPrivateNotifications;
+import static android.app.Flags.redactSensitiveContentNotificationsOnLockscreen;
import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
@@ -654,10 +655,12 @@
!userAllowsPrivateNotificationsInPublic(mCurrentUserId);
boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId);
boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId);
+ boolean isNotifSensitive = redactSensitiveContentNotificationsOnLockscreen()
+ && ent.getRanking() != null && ent.getRanking().hasSensitiveContent();
- // redact notifications if the current user is redacting notifications; however if the
- // notification is associated with a managed profile, we rely on the managed profile
- // setting to determine whether to redact it
+ // redact notifications if the current user is redacting notifications or the notification
+ // contains sensitive content. However if the notification is associated with a managed
+ // profile, we rely on the managed profile setting to determine whether to redact it.
boolean isNotifRedacted = (!isNotifForManagedProfile && isCurrentUserRedactingNotifs)
|| isNotifUserRedacted;
@@ -666,10 +669,11 @@
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
if (keyguardPrivateNotifications()) {
- return !mKeyguardAllowingNotifications
- || userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ return !mKeyguardAllowingNotifications || isNotifSensitive
+ || userForcesRedaction || (notificationRequestsRedaction && isNotifRedacted);
} else {
- return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ return userForcesRedaction || isNotifSensitive
+ || (notificationRequestsRedaction && isNotifRedacted);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 455c964..2e87a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -143,6 +143,12 @@
private var managedUserHandle: UserHandle? = null
private var mSplitShadeEnabled = false
+ var suppressDisconnects = false
+ set(value) {
+ field = value
+ disconnect()
+ }
+
// TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
// how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
@@ -522,6 +528,7 @@
*/
fun disconnect() {
if (!smartspaceViews.isEmpty()) return
+ if (suppressDisconnects) return
execution.assertIsMainThread()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 42bf4e7..071192b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -289,8 +289,9 @@
// for each change, lookup the new value
.map {
secureSettings.getIntForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- UserHandle.USER_CURRENT,
+ name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+ def = 0,
+ userHandle = UserHandle.USER_CURRENT,
) == 1
}
// don't emit anything if nothing has changed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 4c2ef83..4c82bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -37,11 +37,13 @@
import javax.inject.Inject
/**
- * A class which provides an adjustment object to the preparation coordinator which is uses
- * to ensure that notifications are reinflated when ranking-derived information changes.
+ * A class which provides an adjustment object to the preparation coordinator which is uses to
+ * ensure that notifications are reinflated when ranking-derived information changes.
*/
@SysUISingleton
-class NotifUiAdjustmentProvider @Inject constructor(
+class NotifUiAdjustmentProvider
+@Inject
+constructor(
@Main private val handler: Handler,
private val secureSettings: SecureSettings,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -53,14 +55,13 @@
private val dirtyListeners = ListenerSet<Runnable>()
private var isSnoozeSettingsEnabled = false
- /**
- * Update the snooze enabled value on user switch
- */
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- updateSnoozeEnabled()
+ /** Update the snooze enabled value on user switch */
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ updateSnoozeEnabled()
+ }
}
- }
init {
userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
@@ -75,7 +76,7 @@
)
}
updateSnoozeEnabled()
- secureSettings.registerContentObserverForUser(
+ secureSettings.registerContentObserverForUserSync(
SHOW_NOTIFICATION_SNOOZE,
settingsObserver,
UserHandle.USER_ALL
@@ -93,7 +94,7 @@
onSensitiveStateChangedListener
)
}
- secureSettings.unregisterContentObserver(settingsObserver)
+ secureSettings.unregisterContentObserverSync(settingsObserver)
}
}
@@ -104,12 +105,13 @@
private val onSensitiveStateChangedListener = Runnable { dirtyListeners.forEach(Runnable::run) }
- private val settingsObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean) {
- updateSnoozeEnabled()
- dirtyListeners.forEach(Runnable::run)
+ private val settingsObserver =
+ object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean) {
+ updateSnoozeEnabled()
+ dirtyListeners.forEach(Runnable::run)
+ }
}
- }
private fun updateSnoozeEnabled() {
isSnoozeSettingsEnabled =
@@ -126,22 +128,23 @@
}
/**
- * Returns a adjustment object for the given entry. This can be compared to a previous instance
+ * Returns a adjustment object for the given entry. This can be compared to a previous instance
* from the same notification using [NotifUiAdjustment.needReinflate] to determine if it should
* be reinflated.
*/
- fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
- key = entry.key,
- smartActions = entry.ranking.smartActions,
- smartReplies = entry.ranking.smartReplies,
- isConversation = entry.ranking.isConversation,
- isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
- isMinimized = isEntryMinimized(entry),
- needsRedaction =
- lockscreenUserManager.needsRedaction(entry) ||
- (screenshareNotificationHiding() &&
- sensitiveNotifProtectionController.shouldProtectNotification(entry)),
- isChildInGroup = entry.hasEverBeenGroupChild(),
- isGroupSummary = entry.hasEverBeenGroupSummary(),
- )
+ fun calculateAdjustment(entry: NotificationEntry) =
+ NotifUiAdjustment(
+ key = entry.key,
+ smartActions = entry.ranking.smartActions,
+ smartReplies = entry.ranking.smartReplies,
+ isConversation = entry.ranking.isConversation,
+ isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
+ isMinimized = isEntryMinimized(entry),
+ needsRedaction =
+ lockscreenUserManager.needsRedaction(entry) ||
+ (screenshareNotificationHiding() &&
+ sensitiveNotifProtectionController.shouldProtectNotification(entry)),
+ isChildInGroup = entry.hasEverBeenGroupChild(),
+ isGroupSummary = entry.hasEverBeenGroupSummary(),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 42a5bdf..f84b5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -33,8 +33,8 @@
import android.provider.Settings
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
+import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEvent;
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
@@ -86,7 +86,7 @@
}
}
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
/* notifyForDescendants = */ true,
observer
@@ -94,7 +94,7 @@
// QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused.
- observer.onChange(/* selfChange = */ true)
+ observer.onChange(/* selfChange= */ true)
}
}
@@ -243,6 +243,7 @@
override fun shouldSuppress(entry: NotificationEntry) =
keyguardNotificationVisibilityProvider.shouldHideNotification(entry)
}
+
class AvalancheSuppressor(
private val avalancheProvider: AvalancheProvider,
private val systemClock: SystemClock,
@@ -268,38 +269,33 @@
SUPPRESS
}
- enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "An avalanche event occurred but this notification was suppressed by a " +
- "non-avalanche suppressor.")
- START(1802),
-
- @UiEvent(doc = "HUN was suppressed in avalanche.")
- SUPPRESS(1803),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
- ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
- ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a call.")
- ALLOW_CALLSTYLE(1806),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_REMINDER(1807),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_EVENT(1808),
-
- @UiEvent(doc = "HUN allowed during avalanche because it has a full screen intent and " +
- "the full screen intent permission is granted.")
- ALLOW_FSI_WITH_PERMISSION_ON(1809),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
- ALLOW_COLORIZED(1810),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
- ALLOW_EMERGENCY(1811);
+ enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(
+ doc =
+ "An avalanche event occurred but this notification was suppressed by a " +
+ "non-avalanche suppressor."
+ )
+ START(1802),
+ @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803),
+ @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
+ ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
+ ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+ ALLOW_CATEGORY_REMINDER(1807),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+ ALLOW_CATEGORY_EVENT(1808),
+ @UiEvent(
+ doc =
+ "HUN allowed during avalanche because it has a full screen intent and " +
+ "the full screen intent permission is granted."
+ )
+ ALLOW_FSI_WITH_PERMISSION_ON(1809),
+ @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
+ ALLOW_COLORIZED(1810),
+ @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
+ ALLOW_EMERGENCY(1811);
override fun getId(): Int {
return id
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index 332ece4..c7548aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -14,7 +14,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -41,8 +40,8 @@
/** Determines if notifications should be visible based on the state of the keyguard. */
interface KeyguardNotificationVisibilityProvider {
/**
- * Determines if the given notification should be hidden based on the current keyguard state.
- * If a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
+ * Determines if the given notification should be hidden based on the current keyguard state. If
+ * a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
* method may no longer be valid and should be re-queried.
*/
fun shouldHideNotification(entry: NotificationEntry): Boolean
@@ -61,8 +60,9 @@
@Module
interface KeyguardNotificationVisibilityProviderImplModule {
@Binds
- fun bindImpl(impl: KeyguardNotificationVisibilityProviderImpl):
- KeyguardNotificationVisibilityProvider
+ fun bindImpl(
+ impl: KeyguardNotificationVisibilityProviderImpl
+ ): KeyguardNotificationVisibilityProvider
@Binds
@IntoMap
@@ -71,7 +71,9 @@
}
@SysUISingleton
-class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
+class KeyguardNotificationVisibilityProviderImpl
+@Inject
+constructor(
@Main private val handler: Handler,
private val keyguardStateController: KeyguardStateController,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -84,76 +86,88 @@
private val featureFlags: FeatureFlagsClassic
) : CoreStartable, KeyguardNotificationVisibilityProvider {
private val showSilentNotifsUri =
- secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- readShowSilentNotificationSetting()
- if (isLockedOrLocking) {
- // maybe public mode changed
- notifyStateChanged("onUserSwitched")
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ readShowSilentNotificationSetting()
+ if (isLockedOrLocking) {
+ // maybe public mode changed
+ notifyStateChanged("onUserSwitched")
+ }
}
}
- }
override fun start() {
readShowSilentNotificationSetting()
- keyguardStateController.addCallback(object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- notifyStateChanged("onUnlockedChanged")
- }
+ keyguardStateController.addCallback(
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ notifyStateChanged("onUnlockedChanged")
+ }
- override fun onKeyguardShowingChanged() {
- notifyStateChanged("onKeyguardShowingChanged")
+ override fun onKeyguardShowingChanged() {
+ notifyStateChanged("onKeyguardShowingChanged")
+ }
}
- })
- keyguardUpdateMonitor.registerCallback(object : KeyguardUpdateMonitorCallback() {
- override fun onStrongAuthStateChanged(userId: Int) {
- notifyStateChanged("onStrongAuthStateChanged")
+ )
+ keyguardUpdateMonitor.registerCallback(
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onStrongAuthStateChanged(userId: Int) {
+ notifyStateChanged("onStrongAuthStateChanged")
+ }
}
- })
+ )
// register lockscreen settings changed callbacks:
- val settingsObserver: ContentObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- if (uri == showSilentNotifsUri) {
- readShowSilentNotificationSetting()
- }
- if (isLockedOrLocking) {
- notifyStateChanged("Settings $uri changed")
+ val settingsObserver: ContentObserver =
+ object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == showSilentNotifsUri) {
+ readShowSilentNotificationSetting()
+ }
+ if (isLockedOrLocking) {
+ notifyStateChanged("Settings $uri changed")
+ }
}
}
- }
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
- true,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
- globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver)
+ globalSettings.registerContentObserverSync(Settings.Global.ZEN_MODE, settingsObserver)
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
// register (maybe) public mode changed callbacks:
- statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
- override fun onStateChanged(newState: Int) {
- notifyStateChanged("onStatusBarStateChanged")
+ statusBarStateController.addCallback(
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ notifyStateChanged("onStatusBarStateChanged")
+ }
+
+ override fun onUpcomingStateChanged(state: Int) {
+ notifyStateChanged("onStatusBarUpcomingStateChanged")
+ }
}
- override fun onUpcomingStateChanged(state: Int) {
- notifyStateChanged("onStatusBarUpcomingStateChanged")
- }
- })
+ )
userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
}
@@ -169,45 +183,48 @@
onStateChangedListeners.forEach { it.accept(reason) }
}
- override fun shouldHideNotification(entry: NotificationEntry): Boolean = when {
- // Keyguard state doesn't matter if the keyguard is not showing.
- !isLockedOrLocking -> false
- // Notifications not allowed on the lockscreen, always hide.
- !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
- // User settings do not allow this notification on the lockscreen, so hide it.
- userSettingsDisallowNotification(entry) -> true
- // Entry is explicitly marked SECRET, so hide it.
- entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
- // if entry is silent, apply custom logic to see if should hide
- shouldHideIfEntrySilent(entry) -> true
- else -> false
- }
+ override fun shouldHideNotification(entry: NotificationEntry): Boolean =
+ when {
+ // Keyguard state doesn't matter if the keyguard is not showing.
+ !isLockedOrLocking -> false
+ // Notifications not allowed on the lockscreen, always hide.
+ !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
+ // User settings do not allow this notification on the lockscreen, so hide it.
+ userSettingsDisallowNotification(entry) -> true
+ // Entry is explicitly marked SECRET, so hide it.
+ entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
+ // if entry is silent, apply custom logic to see if should hide
+ shouldHideIfEntrySilent(entry) -> true
+ else -> false
+ }
- private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean = when {
- // Show if explicitly high priority (not hidden)
- highPriorityProvider.isExplicitlyHighPriority(entry) -> false
- // Ambient notifications are hidden always from lock screen
- entry.representativeEntry?.isAmbient == true -> true
- // [Now notification is silent]
- // Hide regardless of parent priority if user wants silent notifs hidden
- hideSilentNotificationsOnLockscreen -> true
- // Parent priority is high enough to be shown on the lockscreen, do not hide.
- entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
- // Show when silent notifications are allowed on lockscreen
- else -> false
- }
+ private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
+ when {
+ // Show if explicitly high priority (not hidden)
+ highPriorityProvider.isExplicitlyHighPriority(entry) -> false
+ // Ambient notifications are hidden always from lock screen
+ entry.representativeEntry?.isAmbient == true -> true
+ // [Now notification is silent]
+ // Hide regardless of parent priority if user wants silent notifs hidden
+ hideSilentNotificationsOnLockscreen -> true
+ // Parent priority is high enough to be shown on the lockscreen, do not hide.
+ entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
+ // Show when silent notifications are allowed on lockscreen
+ else -> false
+ }
private fun userSettingsDisallowNotification(entry: NotificationEntry): Boolean {
- fun disallowForUser(user: Int) = when {
- // user is in lockdown, always disallow
- keyguardUpdateMonitor.isUserInLockdown(user) -> true
- // device isn't public, no need to check public-related settings, so allow
- !lockscreenUserManager.isLockscreenPublicMode(user) -> false
- // entry is meant to be secret on the lockscreen, disallow
- isRankingVisibilitySecret(entry) -> true
- // disallow if user disallows notifications in public
- else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
- }
+ fun disallowForUser(user: Int) =
+ when {
+ // user is in lockdown, always disallow
+ keyguardUpdateMonitor.isUserInLockdown(user) -> true
+ // device isn't public, no need to check public-related settings, so allow
+ !lockscreenUserManager.isLockscreenPublicMode(user) -> false
+ // entry is meant to be secret on the lockscreen, disallow
+ isRankingVisibilitySecret(entry) -> true
+ // disallow if user disallows notifications in public
+ else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
+ }
val currentUser = lockscreenUserManager.currentUserId
val notifUser = entry.sbn.user.identifier
return when {
@@ -222,28 +239,35 @@
// ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
// info, and NotificationLockscreenUserManagerImpl is already listening for updates
// to those
- return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
- VISIBILITY_SECRET
+ return entry.ranking.channel != null &&
+ entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
}
- override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
- println("isLockedOrLocking", isLockedOrLocking)
- withIncreasedIndent {
- println("keyguardStateController.isShowing", keyguardStateController.isShowing)
- println("statusBarStateController.currentOrUpcomingState",
- statusBarStateController.currentOrUpcomingState)
+ override fun dump(pw: PrintWriter, args: Array<out String>) =
+ pw.asIndenting().run {
+ println("isLockedOrLocking", isLockedOrLocking)
+ withIncreasedIndent {
+ println("keyguardStateController.isShowing", keyguardStateController.isShowing)
+ println(
+ "statusBarStateController.currentOrUpcomingState",
+ statusBarStateController.currentOrUpcomingState
+ )
+ }
+ println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
}
- println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
- }
- private val isLockedOrLocking get() =
- keyguardStateController.isShowing ||
+ private val isLockedOrLocking
+ get() =
+ keyguardStateController.isShowing ||
statusBarStateController.currentOrUpcomingState == StatusBarState.KEYGUARD
private fun readShowSilentNotificationSetting() {
val showSilentNotifs =
- secureSettings.getBoolForUser(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
- false, UserHandle.USER_CURRENT)
+ secureSettings.getBoolForUser(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+ false,
+ UserHandle.USER_CURRENT
+ )
hideSilentNotificationsOnLockscreen = !showSilentNotifs
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 74925c8..fea360d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -171,11 +171,11 @@
};
if (ENABLE_HEADS_UP) {
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
true,
headsUpObserver);
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true,
headsUpObserver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
index 0fdf681..1efa56f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
@@ -20,8 +20,6 @@
import android.util.Log
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import dagger.Lazy
import javax.inject.Inject
@@ -30,7 +28,6 @@
class NotificationMemoryMonitor
@Inject
constructor(
- private val featureFlags: FeatureFlags,
private val notificationMemoryDumper: NotificationMemoryDumper,
private val notificationMemoryLogger: Lazy<NotificationMemoryLogger>,
) : CoreStartable {
@@ -42,9 +39,6 @@
override fun start() {
Log.d(TAG, "NotificationMemoryMonitor initialized.")
notificationMemoryDumper.init()
- if (featureFlags.isEnabled(Flags.NOTIFICATION_MEMORY_LOGGING_ENABLED)) {
- Log.d(TAG, "Notification memory logging enabled.")
- notificationMemoryLogger.get().init()
- }
+ notificationMemoryLogger.get().init()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
index a17c066..30dbfed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
@@ -92,9 +92,9 @@
synchronized (mListeners) {
if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
for (Uri uri : mListeners.keySet()) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
uri, false, mContentObserver, newUser);
}
}
@@ -131,7 +131,7 @@
mListeners.put(uri, currentListeners);
if (currentListeners.size() == 1) {
mBackgroundHandler.post(() -> {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
uri, false, mContentObserver, mUserTracker.getUserId());
});
}
@@ -159,7 +159,7 @@
if (mListeners.size() == 0) {
mBackgroundHandler.post(() -> {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 6d4301f..e44edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -398,7 +398,7 @@
mSystemIconsContainer.setOnHoverListener(hoverListener);
mView.setOnApplyWindowInsetsListener(
(view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
@@ -416,7 +416,7 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mDisableStateTracker.stopTracking(mCommandQueue);
- mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+ mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
if (mTintedIconManager != null) {
mStatusBarIconController.removeIconGroup(mTintedIconManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index b448d85..fc29eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -413,6 +413,7 @@
afterKeyguardGone: Boolean,
customMessage: String?,
) {
+ Log.i(TAG, "Invoking dismissKeyguardThenExecute, afterKeyguardGone: $afterKeyguardGone")
if (
!action.willRunAnimationOnKeyguard() &&
wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ebb62ec..db4f0af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -112,6 +112,7 @@
import kotlinx.coroutines.Job;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
@@ -899,6 +900,11 @@
} finally {
Trace.endSection();
}
+ } else {
+ Log.w(TAG, "Ignoring request to dismiss, dumping state: ");
+ StringWriter sw = new StringWriter();
+ mKeyguardStateController.dump(new PrintWriter(sw), null);
+ Log.w(TAG, sw.toString());
}
updateStates();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 479aef1..c53558e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -23,9 +23,9 @@
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -162,7 +162,7 @@
this.centralSurfaces = centralSurfaces
updateAnimatorDurationScale()
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
/* notify for descendants */ false,
animatorDurationScaleObserver
@@ -376,8 +376,9 @@
// We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
// already expanded and showing notifications/QS, the animation looks really messy. For now,
// disable it if the notification panel is expanded.
- if ((!this::centralSurfaces.isInitialized ||
- panelExpansionInteractorLazy.get().isPanelExpanded) &&
+ if (
+ (!this::centralSurfaces.isInitialized ||
+ panelExpansionInteractorLazy.get().isPanelExpanded) &&
// Status bar might be expanded because we have started
// playing the animation already
!isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index aac211a..3d8090d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -80,6 +80,10 @@
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
+import kotlinx.coroutines.DisposableHandle;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -90,10 +94,6 @@
import javax.inject.Inject;
-import kotlin.Unit;
-
-import kotlinx.coroutines.DisposableHandle;
-
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -431,7 +431,7 @@
initOngoingCallChip();
mAnimationScheduler.addCallback(this);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
@@ -445,7 +445,7 @@
mStatusBarStateController.removeCallback(this);
mOngoingCallController.removeCallback(mOngoingCallListener);
mAnimationScheduler.removeCallback(this);
- mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+ mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c32f0e8..261258a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -40,6 +40,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
@@ -99,6 +100,7 @@
private val context: Context,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
airplaneModeRepository: AirplaneModeRepository,
// Some "wifi networks" should be rendered as a mobile connection, which is why the wifi
// repository is an input to the mobile repository.
@@ -315,6 +317,7 @@
trySend(false)
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
+ .flowOn(mainDispatcher)
.logDiffsForTable(
tableLogger,
LOGGING_PREFIX,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 4bd8681..fad5df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -138,7 +138,7 @@
}
}
};
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
/* notifyForDescendants = */ false,
settingsObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index 8b63dfe..8123f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -42,16 +42,16 @@
import javax.inject.Inject
@SysUISingleton
-open class DeviceProvisionedControllerImpl @Inject constructor(
+open class DeviceProvisionedControllerImpl
+@Inject
+constructor(
private val secureSettings: SecureSettings,
private val globalSettings: GlobalSettings,
private val userTracker: UserTracker,
private val dumpManager: DumpManager,
@Background private val backgroundHandler: Handler,
@Main private val mainExecutor: Executor
-) : DeviceProvisionedController,
- DeviceProvisionedController.DeviceProvisionedListener,
- Dumpable {
+) : DeviceProvisionedController, DeviceProvisionedController.DeviceProvisionedListener, Dumpable {
companion object {
private const val ALL_USERS = -1
@@ -63,8 +63,7 @@
private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
private val deviceProvisioned = AtomicBoolean(false)
- @GuardedBy("lock")
- private val userSetupComplete = SparseBooleanArray()
+ @GuardedBy("lock") private val userSetupComplete = SparseBooleanArray()
@GuardedBy("lock")
private val listeners = ArraySet<DeviceProvisionedController.DeviceProvisionedListener>()
@@ -81,42 +80,42 @@
return _currentUser
}
- private val observer = object : ContentObserver(backgroundHandler) {
- override fun onChange(
- selfChange: Boolean,
- uris: MutableCollection<Uri>,
- flags: Int,
- userId: Int
- ) {
- val updateDeviceProvisioned = deviceProvisionedUri in uris
- val updateUser = if (userSetupUri in uris) userId else NO_USERS
- updateValues(updateDeviceProvisioned, updateUser)
- if (updateDeviceProvisioned) {
- onDeviceProvisionedChanged()
- }
- if (updateUser != NO_USERS) {
- onUserSetupChanged()
+ private val observer =
+ object : ContentObserver(backgroundHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: MutableCollection<Uri>,
+ flags: Int,
+ userId: Int
+ ) {
+ val updateDeviceProvisioned = deviceProvisionedUri in uris
+ val updateUser = if (userSetupUri in uris) userId else NO_USERS
+ updateValues(updateDeviceProvisioned, updateUser)
+ if (updateDeviceProvisioned) {
+ onDeviceProvisionedChanged()
+ }
+ if (updateUser != NO_USERS) {
+ onUserSetupChanged()
+ }
}
}
- }
- private val userChangedCallback = object : UserTracker.Callback {
- @WorkerThread
- override fun onUserChanged(newUser: Int, userContext: Context) {
- updateValues(updateDeviceProvisioned = false, updateUser = newUser)
- onUserSwitched()
+ private val userChangedCallback =
+ object : UserTracker.Callback {
+ @WorkerThread
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+ onUserSwitched()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {}
}
- override fun onProfilesChanged(profiles: List<UserInfo>) {}
- }
-
init {
userSetupComplete.put(currentUser, false)
}
- /**
- * Call to initialize values and register observers
- */
+ /** Call to initialize values and register observers */
open fun init() {
if (!initted.compareAndSet(false, true)) {
return
@@ -124,31 +123,39 @@
dumpManager.registerDumpable(this)
updateValues()
userTracker.addCallback(userChangedCallback, backgroundExecutor)
- globalSettings.registerContentObserver(deviceProvisionedUri, observer)
- secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
+ globalSettings.registerContentObserverSync(deviceProvisionedUri, observer)
+ secureSettings.registerContentObserverForUserSync(
+ userSetupUri,
+ observer,
+ UserHandle.USER_ALL
+ )
}
@WorkerThread
- private fun updateValues(
- updateDeviceProvisioned: Boolean = true,
- updateUser: Int = ALL_USERS
- ) {
+ private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
if (updateDeviceProvisioned) {
- deviceProvisioned
- .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
+ deviceProvisioned.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
}
synchronized(lock) {
if (updateUser == ALL_USERS) {
val n = userSetupComplete.size()
for (i in 0 until n) {
val user = userSetupComplete.keyAt(i)
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ val value =
+ secureSettings.getIntForUser(
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ user
+ ) != 0
userSetupComplete.put(user, value)
}
} else if (updateUser != NO_USERS) {
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, updateUser) != 0
+ val value =
+ secureSettings.getIntForUser(
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ updateUser
+ ) != 0
userSetupComplete.put(updateUser, value)
}
}
@@ -160,15 +167,11 @@
* The listener will not be called when this happens.
*/
override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- synchronized(lock) {
- listeners.add(listener)
- }
+ synchronized(lock) { listeners.add(listener) }
}
override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- synchronized(lock) {
- listeners.remove(listener)
- }
+ synchronized(lock) { listeners.remove(listener) }
}
override fun isDeviceProvisioned(): Boolean {
@@ -176,20 +179,14 @@
}
override fun isUserSetup(user: Int): Boolean {
- val index = synchronized(lock) {
- userSetupComplete.indexOfKey(user)
- }
+ val index = synchronized(lock) { userSetupComplete.indexOfKey(user) }
return if (index < 0) {
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
- synchronized(lock) {
- userSetupComplete.put(user, value)
- }
+ val value =
+ secureSettings.getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ synchronized(lock) { userSetupComplete.put(user, value) }
value
} else {
- synchronized(lock) {
- userSetupComplete.get(user, false)
- }
+ synchronized(lock) { userSetupComplete.get(user, false) }
}
}
@@ -214,12 +211,8 @@
protected fun dispatchChange(
callback: DeviceProvisionedController.DeviceProvisionedListener.() -> Unit
) {
- val listenersCopy = synchronized(lock) {
- ArrayList(listeners)
- }
- mainExecutor.execute {
- listenersCopy.forEach(callback)
- }
+ val listenersCopy = synchronized(lock) { ArrayList(listeners) }
+ mainExecutor.execute { listenersCopy.forEach(callback) }
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -229,4 +222,4 @@
pw.println("Listeners: $listeners")
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index b07aa81..d210e93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -18,13 +18,16 @@
import android.app.IActivityTaskManager;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.KeyguardStateController.Callback;
+import java.io.PrintWriter;
+
/**
* Source of truth for keyguard state: If locked, occluded, has password, trusted etc.
*/
-public interface KeyguardStateController extends CallbackController<Callback> {
+public interface KeyguardStateController extends CallbackController<Callback>, Dumpable {
/**
* If the device is locked or unlocked.
@@ -40,6 +43,8 @@
return isShowing() && !isOccluded();
}
+ default void dump(PrintWriter pw, String[] args) { }
+
/**
* If the keyguard is showing. This includes when it's occluded by an activity, and when
* the device is asleep or in always on mode, except when the screen timed out and the user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 886010c..c256e64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -36,7 +36,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
-import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -57,7 +56,7 @@
*
*/
@SysUISingleton
-public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable {
+public class KeyguardStateControllerImpl implements KeyguardStateController {
private static final boolean DEBUG_AUTH_WITH_ADB = false;
private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 9eee5d0..f57b696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -115,7 +115,7 @@
};
// Register to listen for changes in Settings.Secure settings.
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, mContentObserver, UserHandle.USER_ALL);
// Register to listen for changes in DeviceConfig settings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
index 40bb67f..9ab8175 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
@@ -188,7 +188,7 @@
});
}
};
- settings.registerContentObserver(
+ settings.registerContentObserverSync(
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
developerOptionsObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 600005b..e09e577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -140,13 +140,13 @@
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (registerZenModeContentObserverBackground()) {
bgHandler.post(() -> {
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
configContentObserver);
});
} else {
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
configContentObserver);
}
updateZenMode(getModeSettingValueFromProvider());
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7494649..4963aae 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -444,7 +444,7 @@
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
UserHandle.ALL);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
false,
new ContentObserver(mBgHandler) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index ec89610..ed52233 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -37,7 +37,6 @@
interface SettingsProxy {
/** Returns the [ContentResolver] this instance was constructed with. */
fun getContentResolver(): ContentResolver
-
/**
* Construct the content URI for a particular name/value pair, useful for monitoring changes
* with a ContentObserver.
@@ -46,42 +45,36 @@
* @return the corresponding content URI, or null if not present
*/
fun getUriFor(name: String): Uri
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
- registerContentObserver(getUriFor(name), settingsObserver)
+ fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
+ registerContentObserverSync(getUriFor(name), settingsObserver)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
- registerContentObserver(uri, false, settingsObserver)
-
+ fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
+ registerContentObserverSync(uri, false, settingsObserver)
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserver(
+ fun registerContentObserverSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
- ) = registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver)
-
+ ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- fun registerContentObserver(
+ fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
) = getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver)
-
/** See [ContentResolver.unregisterContentObserver]. */
- fun unregisterContentObserver(settingsObserver: ContentObserver) =
+ fun unregisterContentObserverSync(settingsObserver: ContentObserver) =
getContentResolver().unregisterContentObserver(settingsObserver)
-
/**
* Look up a name in the database.
*
@@ -89,7 +82,6 @@
* @return the corresponding value, or null if not present
*/
fun getString(name: String): String
-
/**
* Store a name/value pair into the database.
*
@@ -98,7 +90,6 @@
* @return true if the value was set, false on database errors
*/
fun putString(name: String, value: String): Boolean
-
/**
* Store a name/value pair into the database.
*
@@ -129,7 +120,6 @@
* @see .resetToDefaults
*/
fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
-
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -148,7 +138,6 @@
def
}
}
-
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -171,7 +160,6 @@
throw SettingNotFoundException(name)
}
}
-
/**
* Convenience function for updating a single settings value as an integer. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -185,7 +173,6 @@
fun putInt(name: String, value: Int): Boolean {
return putString(name, value.toString())
}
-
/**
* Convenience function for retrieving a single secure settings value as a boolean. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -199,7 +186,6 @@
fun getBool(name: String, def: Boolean): Boolean {
return getInt(name, if (def) 1 else 0) != 0
}
-
/**
* Convenience function for retrieving a single secure settings value as a boolean. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -217,7 +203,6 @@
fun getBool(name: String): Boolean {
return getInt(name) != 0
}
-
/**
* Convenience function for updating a single settings value as a boolean. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -231,7 +216,6 @@
fun putBool(name: String, value: Boolean): Boolean {
return putInt(name, if (value) 1 else 0)
}
-
/**
* Convenience function for retrieving a single secure settings value as a `long`. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -246,7 +230,6 @@
val valString = getString(name)
return parseLongOrUseDefault(valString, def)
}
-
/**
* Convenience function for retrieving a single secure settings value as a `long`. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -265,7 +248,6 @@
val valString = getString(name)
return parseLongOrThrow(name, valString)
}
-
/**
* Convenience function for updating a secure settings value as a long integer. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -279,7 +261,6 @@
fun putLong(name: String, value: Long): Boolean {
return putString(name, value.toString())
}
-
/**
* Convenience function for retrieving a single secure settings value as a floating point
* number. Note that internally setting values are always stored as strings; this function
@@ -294,7 +275,6 @@
val v = getString(name)
return parseFloat(v, def)
}
-
/**
* Convenience function for retrieving a single secure settings value as a float. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -313,7 +293,6 @@
val v = getString(name)
return parseFloatOrThrow(name, v)
}
-
/**
* Convenience function for updating a single settings value as a floating point number. This
* will either create a new entry in the table if the given name does not exist, or modify the
@@ -327,7 +306,6 @@
fun putFloat(name: String, value: Float): Boolean {
return putString(name, value.toString())
}
-
companion object {
/** Convert a string to a long, or uses a default if the string is malformed or null */
@JvmStatic
@@ -341,7 +319,6 @@
}
return value
}
-
/** Convert a string to a long, or throws an exception if the string is malformed or null */
@JvmStatic
@Throws(SettingNotFoundException::class)
@@ -355,7 +332,6 @@
throw SettingNotFoundException(name)
}
}
-
/** Convert a string to a float, or uses a default if the string is malformed or null */
@JvmStatic
fun parseFloat(v: String?, def: Float): Float {
@@ -365,7 +341,6 @@
def
}
}
-
/**
* Convert a string to a float, or throws an exception if the string is malformed or null
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 7484368..d757e33 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -39,9 +39,9 @@
}
}
- names.forEach { name -> registerContentObserverForUser(name, observer, userId) }
+ names.forEach { name -> registerContentObserverForUserSync(name, observer, userId) }
- awaitClose { unregisterContentObserver(observer) }
+ awaitClose { unregisterContentObserverSync(observer) }
}
}
@@ -57,9 +57,9 @@
}
}
- names.forEach { name -> registerContentObserver(name, observer) }
+ names.forEach { name -> registerContentObserverSync(name, observer) }
- awaitClose { unregisterContentObserver(observer) }
+ awaitClose { unregisterContentObserverSync(observer) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 2285270..ed13943 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -41,10 +41,8 @@
* instances, unifying setting related actions in one place.
*/
interface UserSettingsProxy : SettingsProxy {
-
/** Returns that [UserTracker] this instance was constructed with. */
val userTracker: UserTracker
-
/** Returns the user id for the associated [ContentResolver]. */
var userId: Int
get() = getContentResolver().userId
@@ -53,7 +51,6 @@
"userId cannot be set in interface, use setter from an implementation instead."
)
}
-
/**
* Returns the actual current user handle when querying with the current user. Otherwise,
* returns the passed in user id.
@@ -63,63 +60,57 @@
userHandle
} else userTracker.userId
}
-
- override fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
- registerContentObserverForUser(uri, settingsObserver, userId)
+ override fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
+ registerContentObserverForUserSync(uri, settingsObserver, userId)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- override fun registerContentObserver(
+ override fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
) {
- registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, userId)
+ registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver]
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
name: String,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(getUriFor(name), settingsObserver, userHandle)
+ registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(uri, false, settingsObserver, userHandle)
+ registerContentObserverForUserSync(uri, false, settingsObserver, userHandle)
}
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver]
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(
+ registerContentObserverForUserSync(
getUriFor(name),
notifyForDescendants,
settingsObserver,
userHandle
)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -136,7 +127,6 @@
Unit
}
}
-
/**
* Look up a name in the database.
*
@@ -146,10 +136,8 @@
override fun getString(name: String): String {
return getStringForUser(name, userId)
}
-
/** See [getString]. */
fun getStringForUser(name: String, userHandle: Int): String
-
/**
* Store a name/value pair into the database. Values written by this method will be overridden
* if a restore happens in the future.
@@ -162,10 +150,8 @@
override fun putString(name: String, value: String): Boolean {
return putStringForUser(name, value, userId)
}
-
/** Similar implementation to [putString] for the specified [userHandle]. */
fun putStringForUser(name: String, value: String, userHandle: Int): Boolean
-
/** Similar implementation to [putString] for the specified [userHandle]. */
fun putStringForUser(
name: String,
@@ -175,11 +161,9 @@
@UserIdInt userHandle: Int,
overrideableByRestore: Boolean
): Boolean
-
override fun getInt(name: String, def: Int): Int {
return getIntForUser(name, def, userId)
}
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
val v = getStringForUser(name, userHandle)
@@ -189,10 +173,8 @@
def
}
}
-
@Throws(SettingNotFoundException::class)
override fun getInt(name: String) = getIntForUser(name, userId)
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getIntForUser(name: String, userHandle: Int): Int {
@@ -203,66 +185,52 @@
throw SettingNotFoundException(name)
}
}
-
override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
fun putIntForUser(name: String, value: Int, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
-
override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
-
/** Similar implementation to [getBool] for the specified [userHandle]. */
fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
getIntForUser(name, if (def) 1 else 0, userHandle) != 0
-
@Throws(SettingNotFoundException::class)
override fun getBool(name: String) = getBoolForUser(name, userId)
-
/** Similar implementation to [getBool] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getBoolForUser(name: String, userHandle: Int): Boolean {
return getIntForUser(name, userHandle) != 0
}
-
override fun putBool(name: String, value: Boolean): Boolean {
return putBoolForUser(name, value, userId)
}
-
/** Similar implementation to [putBool] for the specified [userHandle]. */
fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
putIntForUser(name, if (value) 1 else 0, userHandle)
-
/** Similar implementation to [getLong] for the specified [userHandle]. */
fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrUseDefault(valString, def)
}
-
/** Similar implementation to [getLong] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getLongForUser(name: String, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrThrow(name, valString)
}
-
/** Similar implementation to [putLong] for the specified [userHandle]. */
fun putLongForUser(name: String, value: Long, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
-
/** Similar implementation to [getFloat] for the specified [userHandle]. */
fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloat(v, def)
}
-
/** Similar implementation to [getFloat] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getFloatForUser(name: String, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloatOrThrow(name, v)
}
-
/** Similar implementation to [putFloat] for the specified [userHandle]. */
fun putFloatForUser(name: String, value: Float, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index e0f64b4..154737c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -17,15 +17,18 @@
package com.android.systemui.volume.domain.interactor
import android.bluetooth.BluetoothAdapter
+import android.content.Context
import android.media.AudioDeviceInfo
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.MediaDevice.MediaDeviceType
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioSharingRepository
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
@@ -36,6 +39,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -47,6 +51,7 @@
class AudioOutputInteractor
@Inject
constructor(
+ @Application private val context: Context,
audioRepository: AudioRepository,
audioModeInteractor: AudioModeInteractor,
@VolumePanelScope scope: CoroutineScope,
@@ -58,7 +63,7 @@
audioSharingRepository: AudioSharingRepository,
) {
- val currentAudioDevice: Flow<AudioOutputDevice> =
+ val currentAudioDevice: StateFlow<AudioOutputDevice> =
audioModeInteractor.isOngoingCall
.flatMapLatest { isOngoingCall ->
if (isOngoingCall) {
@@ -102,7 +107,7 @@
)
}
return AudioOutputDevice.BuiltIn(
- name = productName.toString(),
+ name = PhoneMediaDevice.getMediaTransferThisDeviceName(context),
icon = deviceIconInteractor.loadIcon(type),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
index 199bc3b..333f4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
@@ -21,7 +21,7 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.media.dialog.MediaOutputDialogManager
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
@@ -29,14 +29,12 @@
@VolumePanelScope
class MediaOutputActionsInteractor
@Inject
-constructor(
- private val mediaOutputDialogManager: MediaOutputDialogManager,
-) {
+constructor(private val mediaOutputDialogManager: MediaOutputDialogManager) {
- fun onBarClick(sessionWithPlaybackState: SessionWithPlaybackState?, expandable: Expandable?) {
- if (sessionWithPlaybackState?.isPlaybackActive == true) {
+ fun onBarClick(model: MediaOutputComponentModel?, expandable: Expandable?) {
+ if (model is MediaOutputComponentModel.MediaSession) {
mediaOutputDialogManager.createAndShowWithController(
- sessionWithPlaybackState.session.packageName,
+ model.session.packageName,
false,
expandable?.dialogController()
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
new file mode 100644
index 0000000..ed25129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.Result
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.android.systemui.volume.panel.shared.model.wrapInResult
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
+
+/** Gathers together a domain state for the Media Output Volume Panel component. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class MediaOutputComponentInteractor
+@Inject
+constructor(
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ audioOutputInteractor: AudioOutputInteractor,
+ audioModeInteractor: AudioModeInteractor,
+ interactor: MediaOutputInteractor,
+) {
+
+ private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
+ interactor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
+ playback?.let { SessionWithPlaybackState(session, playback.isActive) }
+ }
+ }
+ }
+ .wrapInResult()
+ .stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ Result.Loading(),
+ )
+
+ private val currentAudioDevice: Flow<AudioOutputDevice> =
+ audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unknown }
+
+ val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
+ audioModeInteractor.isOngoingCall
+ .flatMapLatest { isOngoingCall ->
+ audioOutputInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
+ if (isOngoingCall) {
+ currentAudioDevice.map {
+ MediaOutputComponentModel.Calling(it, isInAudioSharing)
+ }
+ } else {
+ combine(sessionWithPlaybackState.filterData(), currentAudioDevice) {
+ sessionWithPlaybackState,
+ currentAudioDevice ->
+ if (sessionWithPlaybackState == null) {
+ MediaOutputComponentModel.Idle(currentAudioDevice, isInAudioSharing)
+ } else {
+ MediaOutputComponentModel.MediaSession(
+ sessionWithPlaybackState.session,
+ sessionWithPlaybackState.isPlaybackActive,
+ currentAudioDevice,
+ isInAudioSharing,
+ )
+ }
+ }
+ }
+ }
+ }
+ .wrapInResult()
+ .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
new file mode 100644
index 0000000..220fb2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.volume.panel.component.mediaoutput.domain.model
+
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
+
+/** Models domain data for the Media Output Component */
+sealed interface MediaOutputComponentModel {
+
+ val device: AudioOutputDevice
+ val isInAudioSharing: Boolean
+
+ /** There is an ongoing call on the device. */
+ data class Calling(
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+
+ /** There is media playing on the device. */
+ data class MediaSession(
+ val session: MediaDeviceSession,
+ val isPlaybackActive: Boolean,
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+
+ /** There is nothing playing on the device. */
+ data class Idle(
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 40b7977..36b42f2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -18,33 +18,24 @@
import android.content.Context
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
-import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputComponentInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
-import com.android.systemui.volume.panel.shared.model.wrapInResult
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
/** Models the UI of the Media Output Volume Panel component. */
@@ -56,61 +47,40 @@
private val context: Context,
@VolumePanelScope private val coroutineScope: CoroutineScope,
private val actionsInteractor: MediaOutputActionsInteractor,
- private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
- private val audioOutputInteractor: AudioOutputInteractor,
- audioModeInteractor: AudioModeInteractor,
- interactor: MediaOutputInteractor,
+ private val mediaOutputComponentInteractor: MediaOutputComponentInteractor,
private val uiEventLogger: UiEventLogger,
) {
- private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
- interactor.defaultActiveMediaSession
- .filterData()
- .flatMapLatest { session ->
- if (session == null) {
- flowOf(null)
- } else {
- mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
- playback?.let { SessionWithPlaybackState(session, playback.isActive) }
- }
- }
- }
- .wrapInResult()
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- Result.Loading(),
- )
-
val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
- combine(
- sessionWithPlaybackState.filterData(),
- audioModeInteractor.isOngoingCall,
- audioOutputInteractor.currentAudioDevice.filter {
- it !is AudioOutputDevice.Unknown
- },
- audioOutputInteractor.isInAudioSharing,
- ) { mediaDeviceSession, isOngoingCall, currentConnectedDevice, isInAudioSharing ->
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { mediaOutputModel ->
val label =
- when {
- isOngoingCall -> context.getString(R.string.media_output_title_ongoing_call)
- mediaDeviceSession?.isPlaybackActive == true ->
- context.getString(
- R.string.media_output_label_title,
- mediaDeviceSession.session.appLabel
- )
- else -> context.getString(R.string.media_output_title_without_playing)
+ when (mediaOutputModel) {
+ is MediaOutputComponentModel.Idle -> {
+ context.getString(R.string.media_output_title_without_playing)
+ }
+ is MediaOutputComponentModel.MediaSession -> {
+ if (mediaOutputModel.isPlaybackActive) {
+ context.getString(
+ R.string.media_output_label_title,
+ mediaOutputModel.session.appLabel,
+ )
+ } else {
+ context.getString(R.string.media_output_title_without_playing)
+ }
+ }
+ is MediaOutputComponentModel.Calling -> {
+ context.getString(R.string.media_output_title_ongoing_call)
+ }
}
ConnectedDeviceViewModel(
label,
- when (isInAudioSharing) {
- true -> {
- context.getString(R.string.audio_sharing_description)
- }
- false -> {
- currentConnectedDevice.name
- }
- }
+ if (mediaOutputModel.isInAudioSharing) {
+ context.getString(R.string.audio_sharing_description)
+ } else {
+ mediaOutputModel.device.name
+ },
)
}
.stateIn(
@@ -120,16 +90,20 @@
)
val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
- combine(sessionWithPlaybackState.filterData(), audioOutputInteractor.currentAudioDevice) {
- mediaDeviceSession,
- currentConnectedDevice ->
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { mediaOutputModel ->
val icon: Icon =
- currentConnectedDevice
- .takeIf { currentConnectedDevice !is AudioOutputDevice.Unknown }
+ mediaOutputModel.device
+ .takeIf { it !is AudioOutputDevice.Unknown }
?.icon
?.let { Icon.Loaded(it, null) }
?: Icon.Resource(R.drawable.ic_media_home_devices, null)
- if (mediaDeviceSession?.isPlaybackActive == true) {
+ val isPlaybackActive =
+ (mediaOutputModel as? MediaOutputComponentModel.MediaSession)
+ ?.isPlaybackActive == true
+ val isCalling = mediaOutputModel is MediaOutputComponentModel.Calling
+ if (isPlaybackActive || isCalling) {
DeviceIconViewModel.IsPlaying(
icon = icon,
iconColor =
@@ -156,8 +130,9 @@
)
val enabled: StateFlow<Boolean> =
- audioOutputInteractor.isInAudioSharing
- .map { !it }
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { !it.isInAudioSharing }
.stateIn(
coroutineScope,
SharingStarted.Eagerly,
@@ -166,7 +141,11 @@
fun onBarClick(expandable: Expandable?) {
uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
- val result = sessionWithPlaybackState.value
- actionsInteractor.onBarClick((result as? Result.Data)?.data, expandable)
+ val result: Result<MediaOutputComponentModel> =
+ mediaOutputComponentInteractor.mediaOutputModel.value
+ actionsInteractor.onBarClick(
+ (result as? Result.Data<MediaOutputComponentModel>)?.data,
+ expandable
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 4b2d26a..6c6a1cc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -48,15 +48,31 @@
private val uiEventLogger: UiEventLogger,
) {
+ private val spatialSpeakerIcon =
+ Icon.Resource(R.drawable.ic_spatial_speaker, contentDescription = null)
+
val spatialAudioButton: StateFlow<ButtonViewModel?> =
- interactor.isEnabled
- .map {
- val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled
- it.toViewModel(isChecked)
+ combine(interactor.isEnabled, interactor.isAvailable) { isEnabled, isAvailable ->
+ isEnabled
+ .toViewModel(
+ isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
+ isHeadTrackingAvailable =
+ isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+ )
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
.stateIn(scope, SharingStarted.Eagerly, null)
+ val shouldUsePopup: StateFlow<Boolean> =
+ interactor.isAvailable
+ .map {
+ // head tracking availability means there are three possible states for the spatial
+ // audio: disabled, enabled regular, enabled with head tracking.
+ // Show popup in this case instead of a togglealbe button.
+ it is SpatialAudioAvailabilityModel.SpatialAudio
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
val isAvailable: StateFlow<Boolean> =
availabilityCriteria.isAvailable().stateIn(scope, SharingStarted.Eagerly, true)
@@ -73,8 +89,12 @@
}
}
.map { isEnabled ->
- val isChecked = isEnabled == currentIsEnabled
- val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked)
+ val buttonViewModel: ButtonViewModel =
+ isEnabled.toViewModel(
+ isChecked = isEnabled == currentIsEnabled,
+ isHeadTrackingAvailable =
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
+ )
SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled)
}
}
@@ -97,11 +117,21 @@
scope.launch { interactor.setEnabled(model) }
}
- private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel {
+ private fun SpatialAudioEnabledModel.toViewModel(
+ isChecked: Boolean,
+ isHeadTrackingAvailable: Boolean,
+ ): ButtonViewModel {
+ // This method deliberately uses the same icon for the case when head tracking is disabled
+ // to show a toggle button with a non-changing icon
if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_tracking)
)
}
@@ -109,7 +139,12 @@
if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_fixed)
)
}
@@ -117,7 +152,12 @@
if (this is SpatialAudioEnabledModel.Disabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_off)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index ff18418..1d32a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -149,12 +149,12 @@
if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) {
mWalletPreferenceChangeEvents--;
if (mWalletPreferenceChangeEvents == 0) {
- mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+ mSecureSettings.unregisterContentObserverSync(mWalletPreferenceObserver);
}
} else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) {
mDefaultPaymentAppChangeEvents--;
if (mDefaultPaymentAppChangeEvents == 0) {
- mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver);
+ mSecureSettings.unregisterContentObserverSync(mDefaultPaymentAppObserver);
}
} else if (event == DEFAULT_WALLET_APP_CHANGE && mDefaultWalletAppObserver != null) {
mDefaultWalletAppChangeEvents--;
@@ -312,7 +312,7 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
false /* notifyForDescendants */,
mDefaultPaymentAppObserver,
@@ -351,7 +351,7 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
QuickAccessWalletClientImpl.SETTING_KEY,
false /* notifyForDescendants */,
mWalletPreferenceObserver,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 8c4179d..0a3225e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -161,8 +161,11 @@
doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor)
.removeCallback(any(KeyguardUpdateMonitorCallback.class));
- mCarrierTextCallbackInfo = new CarrierTextManager.CarrierTextCallbackInfo("",
- new CharSequence[]{}, false, new int[]{});
+ mCarrierTextCallbackInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+ /* carrierText= */ "",
+ /* listOfCarriers= */ new CharSequence[]{},
+ /* anySimReady= */ false,
+ /* subscriptionIds= */ new int[]{});
when(mTelephonyManager.getSupportedModemCount()).thenReturn(3);
when(mTelephonyManager.getActiveModemCount()).thenReturn(3);
@@ -473,7 +476,7 @@
}
@Test
- public void carrierText_satelliteTextNull_notUsed() {
+ public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed() {
reset(mCarrierTextCallback);
List<SubscriptionInfo> list = new ArrayList<>();
list.add(TEST_SUBSCRIPTION);
@@ -491,12 +494,38 @@
CarrierTextManager.CarrierTextCallbackInfo.class);
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
- // THEN the default subscription carrier text is used
+ // THEN satellite mode is false and the default subscription carrier text is used
verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+ assertThat(captor.getValue().isInSatelliteMode).isFalse();
assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER);
}
@Test
+ public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed() {
+ reset(mCarrierTextCallback);
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
+ TelephonyManager.SIM_STATE_READY);
+ when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list);
+ mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+ // WHEN the satellite text is non-null
+ mSatelliteViewModel.getCarrierText().setValue("Satellite Test Text");
+ mTestScope.getTestScheduler().runCurrent();
+
+ ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextManager.CarrierTextCallbackInfo.class);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
+
+ // THEN satellite mode is true and the satellite text is used
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+ assertThat(captor.getValue().isInSatelliteMode).isTrue();
+ assertThat(captor.getValue().carrierText).isEqualTo("Satellite Test Text");
+ }
+
+ @Test
public void carrierText_satelliteTextUpdates_autoTriggersCallback() {
reset(mCarrierTextCallback);
List<SubscriptionInfo> list = new ArrayList<>();
@@ -517,6 +546,7 @@
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
// AND use the satellite text as the carrier text
+ assertThat(captor.getValue().isInSatelliteMode).isTrue();
assertThat(captor.getValue().carrierText).isEqualTo("Test satellite text");
// WHEN the satellite text is reset to null
@@ -528,6 +558,7 @@
// that doesn't include the satellite info
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+ assertThat(captor.getValue().isInSatelliteMode).isFalse();
assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER);
}
@@ -566,10 +597,11 @@
mCarrierTextManager.setListening(mCarrierTextCallback);
// THEN we should automatically re-trigger #updateCarrierText and get callback info
- // that includes the new satellite text
+ // that includes the new satellite state and text
mTestScope.getTestScheduler().runCurrent();
FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+ assertThat(captor.getValue().isInSatelliteMode).isTrue();
assertThat(captor.getValue().carrierText).isEqualTo("New satellite text");
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 99b5a4b..cfa74f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -181,7 +181,7 @@
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
ContentObserver observer = observerCaptor.getValue();
@@ -247,7 +247,7 @@
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
observerCaptor.capture(), eq(UserHandle.USER_ALL));
ContentObserver observer = observerCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 92b06ba..138fed2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -393,7 +393,7 @@
mWindowMagnificationSettings.showSettingPanel();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(ACCESSIBILITY_MAGNIFICATION_CAPABILITY),
any(ContentObserver.class),
eq(UserHandle.USER_CURRENT));
@@ -408,7 +408,7 @@
mWindowMagnificationSettings.showSettingPanel();
mWindowMagnificationSettings.hideSettingPanel();
- verify(mSecureSettings).unregisterContentObserver(any(ContentObserver.class));
+ verify(mSecureSettings).unregisterContentObserverSync(any(ContentObserver.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 9ba56d2..6391986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -82,7 +82,12 @@
val keys =
captureMany<String> {
verify(secureSettings)
- .registerContentObserverForUser(capture(), anyBoolean(), any(), eq(USER_ID))
+ .registerContentObserverForUserSync(
+ capture(),
+ anyBoolean(),
+ any(),
+ eq(USER_ID)
+ )
}
assertThat(keys).containsExactly(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION)
@@ -102,7 +107,7 @@
val observer =
withArgCaptor<ContentObserver> {
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(setting),
anyBoolean(),
capture(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
index 590989d..154c373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
@@ -332,7 +332,7 @@
}
private fun attachRepositoryToSettings() {
- secureSettings.registerContentObserver(
+ secureSettings.registerContentObserverSync(
SETTING_SHOW,
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
@@ -343,7 +343,7 @@
}
)
- secureSettings.registerContentObserver(
+ secureSettings.registerContentObserverSync(
SETTING_ACTION,
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index 84b1c00..87dd9b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -21,11 +21,11 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
* Tests for {@link ListGridLayout}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class GlobalActionsGridLayoutTest extends SysuiTestCase {
private GlobalActionsGridLayout mGridLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
index 16dcd65..ea0f4d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
@@ -24,11 +24,11 @@
import static org.mockito.Mockito.spy;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.MultiListLayout;
@@ -44,7 +44,7 @@
* Tests for {@link ListGridLayout}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class GlobalActionsLayoutTest extends SysuiTestCase {
private TestLayout mLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
index 4c65b90..a10457f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
@@ -18,11 +18,11 @@
import static junit.framework.Assert.assertEquals;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
* Tests for {@link ListGridLayout}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ListGridLayoutTest extends SysuiTestCase {
private ListGridLayout mListGridLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
index 28c01ad..73509e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
@@ -26,8 +26,8 @@
import android.os.PowerManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -41,7 +41,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ShutdownUiTest extends SysuiTestCase {
ShutdownUi mShutdownUi;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
index d7a0d5b5..64cd091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.backlight.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -29,11 +30,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyboardBacklightInteractorTest : SysuiTestCase() {
private val keyboardRepository = FakeKeyboardRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
index 7207fbf..47261a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.backlight.ui
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
@@ -37,7 +38,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -46,7 +46,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyboardBacklightDialogCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
index 4410e68..53bcf86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.hardware.input.InputManager.KeyboardBacklightListener
import android.hardware.input.KeyboardBacklightState
import android.view.InputDevice
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
@@ -44,7 +45,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -53,7 +53,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyboardRepositoryTest : SysuiTestCase() {
@Captor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
index 05a2ca2..0757ea1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.shortcut.ui
import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyboard.shortcut.fakeShortcutHelperStartActivity
@@ -33,11 +34,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class ShortcutHelperActivityStarterTest : SysuiTestCase() {
private val kosmos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 44a8904..34b2aaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyboard.shortcut.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -34,11 +35,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class ShortcutHelperViewModelTest : SysuiTestCase() {
private val kosmos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index 59d8fc3..9a721fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyboard.stickykeys.ui
import android.app.Dialog
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyboard.data.repository.FakeStickyKeysRepository
@@ -36,13 +37,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: StickyKeysIndicatorCoordinator
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 d14d72d..9d9e5be62 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
@@ -19,6 +19,7 @@
import android.hardware.input.InputManager
import android.hardware.input.StickyModifierState
import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -47,14 +48,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 5b836b6..925ace2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -36,9 +36,9 @@
import android.content.res.ColorStateList;
import android.graphics.Color;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.logging.KeyguardLogger;
@@ -56,7 +56,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
@SmallTest
public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
index b44fb8e..325bae1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
@@ -24,10 +24,10 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class KeyguardIndicationTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index ce8028c..909acca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -34,7 +34,6 @@
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -44,6 +43,7 @@
import androidx.slice.SliceSpecs;
import androidx.slice.builders.ListBuilder;
import androidx.slice.core.SliceQuery;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -72,7 +72,7 @@
import java.util.concurrent.TimeUnit;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class KeyguardSliceProviderTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 6ebda4d..128dd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -7,7 +7,6 @@
import android.graphics.Rect
import android.os.PowerManager
import android.platform.test.annotations.DisableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
@@ -15,6 +14,7 @@
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.Flags
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations
import java.util.function.Predicate
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@SmallTest
class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 977116e..144e634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -1,20 +1,15 @@
package com.android.systemui.keyguard
import android.content.ComponentCallbacks2
-import android.graphics.HardwareRenderer
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testDispatcher
@@ -26,7 +21,6 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
import com.android.systemui.utils.GlobalWindowManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -43,7 +37,7 @@
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ResourceTrimmerTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -62,52 +56,21 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
-
- val withDeps =
- KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- repository = keyguardRepository,
- )
- val keyguardInteractor = withDeps.keyguardInteractor
resourceTrimmer =
ResourceTrimmer(
- keyguardInteractor,
- powerInteractor = powerInteractor,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
globalWindowManager = globalWindowManager,
applicationScope = testScope.backgroundScope,
bgDispatcher = kosmos.testDispatcher,
- featureFlags = featureFlags,
sceneInteractor = kosmos.sceneInteractor,
)
resourceTrimmer.start()
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun noChange_noOutputChanges() =
- testScope.runTest {
- testScope.runCurrent()
- verifyZeroInteractions(globalWindowManager)
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun dozeAodDisabled_sleep_trimsMemory() =
- testScope.runTest {
- powerInteractor.setAsleepForTest()
- testScope.runCurrent()
- verify(globalWindowManager, times(1))
- .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
- }
-
- @Test
- @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun dozeAodDisabled_flagDisabled_sleep_doesntTrimMemory() =
+ fun dozeAodDisabled_sleep_doesntTrimMemory() =
testScope.runTest {
powerInteractor.setAsleepForTest()
testScope.runCurrent()
@@ -115,8 +78,7 @@
}
@Test
- @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun dozeEnabled_flagDisabled_sleepWithFullDozeAmount_doesntTrimMemory() =
+ fun dozeEnabled_sleepWithFullDozeAmount_doesntTrimMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
keyguardRepository.setDozeAmount(1f)
@@ -126,20 +88,6 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() =
- testScope.runTest {
- keyguardRepository.setDreaming(true)
- keyguardRepository.setDozeAmount(1f)
- powerInteractor.setAsleepForTest()
- testScope.runCurrent()
- verify(globalWindowManager, times(1))
- .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -150,34 +98,6 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() {
- testScope.runTest {
- keyguardRepository.setDreaming(true)
- keyguardRepository.setDozeAmount(0f)
- powerInteractor.setAsleepForTest()
-
- testScope.runCurrent()
- verifyZeroInteractions(globalWindowManager)
-
- generateSequence(0f) { it + 0.1f }
- .takeWhile { it < 1f }
- .forEach {
- keyguardRepository.setDozeAmount(it)
- testScope.runCurrent()
- }
- verifyZeroInteractions(globalWindowManager)
-
- keyguardRepository.setDozeAmount(1f)
- testScope.runCurrent()
- verify(globalWindowManager, times(1))
- .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
- }
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -209,7 +129,6 @@
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
@DisableSceneContainer
fun keyguardTransitionsToGone_trimsFontCache() =
testScope.runTest {
@@ -220,12 +139,10 @@
)
verify(globalWindowManager, times(1))
.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
verifyNoMoreInteractions(globalWindowManager)
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
@EnableSceneContainer
fun keyguardTransitionsToGone_trimsFontCache_scene_container() =
testScope.runTest {
@@ -233,38 +150,6 @@
verify(globalWindowManager, times(1))
.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
verifyNoMoreInteractions(globalWindowManager)
}
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- @DisableSceneContainer
- fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
- testScope.runTest {
- featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GONE,
- testScope
- )
- // Memory hidden should still be called.
- verify(globalWindowManager, times(1))
- .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(0)).trimCaches(any())
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
- @EnableSceneContainer
- fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache_scene_container() =
- testScope.runTest {
- featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
- kosmos.setSceneTransition(Idle(Scenes.Gone))
-
- // Memory hidden should still be called.
- verify(globalWindowManager, times(1))
- .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
- verify(globalWindowManager, times(0)).trimCaches(any())
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
index 70a0415..99ff2d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
@@ -21,8 +21,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScreenLifecycleTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 39a453d..a9f7d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -24,8 +24,8 @@
import android.app.IWallpaperManager;
import android.os.PowerManager;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class WakefulnessLifecycleTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
index c7f1dec..d435a47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
@@ -25,8 +25,8 @@
import android.os.Looper
import android.os.UserHandle
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
index bd4525b..47bf653 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
@@ -18,16 +18,16 @@
import android.content.Intent
import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
index df1833e..5fa194d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
@@ -203,4 +203,4 @@
verify(ringerModeInternal).removeObserver(any())
coroutineContext.cancelChildren()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
index f5b5261..972ca02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.data.repository
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
@@ -37,11 +38,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBlueprintRepository
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 9aac8e2..af5187d 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
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.ClockEventController
import com.android.systemui.SysuiTestCase
@@ -36,11 +37,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardClockRepositoryTest : SysuiTestCase() {
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 a320845..8b8a6cb 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
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -31,10 +32,9 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardSmartspaceRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 8be1e7a..a8271c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.os.Handler
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
@@ -56,7 +57,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnit
@@ -64,7 +64,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 14d9548..d2a9c58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -63,11 +63,11 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
@@ -83,7 +83,7 @@
detail = "on certain architectures all permutations with startActivity=true is causing failures"
)
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@DisableSceneContainer
class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index ced3526..9d06031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -63,11 +63,11 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
@@ -83,7 +83,7 @@
detail = "on certain architectures all permutations with startActivity=true is causing failures"
)
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@EnableSceneContainer
class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
index 8a0613f..baa8ed7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.view.layout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
@@ -28,7 +29,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
@@ -36,7 +36,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardBlueprintCommandListenerTest : SysuiTestCase() {
private lateinit var keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 344e0fc..9fab0d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
@@ -54,7 +54,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@ExperimentalCoroutinesApi
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index b3cc5c9..4a39a9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -22,6 +22,7 @@
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -52,12 +53,11 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ClockSectionTest : SysuiTestCase() {
private lateinit var underTest: ClockSection
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 4f2b690..693a877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -21,6 +21,7 @@
import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.LegacyLockIconViewController
import com.android.systemui.Flags as AConfigFlags
@@ -44,14 +45,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DefaultDeviceEntrySectionTest : SysuiTestCase() {
@Mock private lateinit var authController: AuthController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 711f90f..10f7128 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -20,6 +20,7 @@
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
@@ -30,11 +31,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index be10b82..7d4f034 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -22,6 +22,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.GONE
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -41,11 +42,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class SmartspaceSectionTest : SysuiTestCase() {
private lateinit var underTest: SmartspaceSection
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index f1c93c4..e44bc7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,11 +36,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.verify
@ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AlternateBouncerViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 391831a..6398a5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
@@ -37,11 +38,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.kotlin.whenever
@ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
index ec2a1d3..129752e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.os.fakeExecutorHandler
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -25,12 +26,11 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardBlueprintViewModelTest : SysuiTestCase() {
@Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
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 c1e3e84..e33d75c 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
@@ -20,6 +20,7 @@
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags as AConfigFlags
@@ -49,7 +50,6 @@
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.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
@@ -76,7 +76,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
@@ -84,7 +83,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@@ -290,7 +289,6 @@
underTest =
KeyguardQuickAffordancesCombinedViewModel(
- applicationScope = kosmos.applicationCoroutineScope,
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
index 78f4dcc..0c3fcb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -32,13 +33,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardSmartspaceViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index 447b333..fbeb6d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -32,9 +32,9 @@
import static org.mockito.Mockito.when;
import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
@@ -54,7 +54,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class SessionTrackerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
index ab19b3a..d2e6dad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.log.core
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogMessageImpl
@@ -16,10 +17,9 @@
import org.mockito.Mockito.isNull
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
@SmallTest
-@RunWith(MockitoJUnitRunner::class)
+@RunWith(AndroidJUnit4::class)
class LoggerTest : SysuiTestCase() {
@Mock private lateinit var buffer: MessageBuffer
private lateinit var message: LogMessage
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index 5986f4a..e2a2b7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -18,8 +18,8 @@
import android.app.smartspace.SmartspaceAction
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -64,7 +64,7 @@
private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class LegacyMediaDataFilterImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index dd05a0d..544350c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -26,9 +26,9 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
@@ -48,7 +48,7 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class MediaDataCombineLatestTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index caaa42f..8471fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -18,8 +18,8 @@
import android.app.smartspace.SmartspaceAction
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -76,7 +76,7 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaDataFilterImplTest : SysuiTestCase() {
val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 16d8819..42bd46f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -31,8 +31,8 @@
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -86,7 +86,7 @@
private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 31a2435..efe4413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -20,8 +20,8 @@
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
import android.media.session.MediaSessionManager
-import android.testing.AndroidTestingRunner
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.media.controls.MediaTestUtils
@@ -53,7 +53,7 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, notificationKey = NOTIF_KEY)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
public class MediaSessionBasedFilterTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 6ca0bef..c1bba4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -20,7 +20,7 @@
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
@@ -65,7 +65,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MediaTimeoutListenerTest : SysuiTestCase() {
@Mock private lateinit var mediaControllerFactory: MediaControllerFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 55ff231..02d7413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -27,8 +27,8 @@
import android.media.MediaDescription
import android.media.session.MediaSession
import android.provider.Settings
-import android.testing.AndroidTestingRunner
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.broadcast.BroadcastDispatcher
@@ -76,7 +76,7 @@
private fun <T> any(): T = Mockito.any<T>()
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaResumeListenerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
index 8dfa5b8..dca19690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
@@ -23,8 +23,8 @@
import android.media.session.MediaController
import android.media.session.MediaSession
import android.service.media.MediaBrowserService
-import android.testing.AndroidTestingRunner
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.util.mockito.mock
@@ -53,7 +53,7 @@
private fun <T> any(): T = Mockito.any<T>()
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
public class ResumeMediaBrowserTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
index b509e77..addf008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.media.controls.ui
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
@@ -34,7 +34,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
public class MediaPlayerDataTest : SysuiTestCase() {
@Mock private lateinit var playerIsPlaying: MediaControlPanel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
index 4fcd3bb..cdcb143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
@@ -18,8 +18,8 @@
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert.assertFalse
@@ -37,7 +37,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AnimationBindHandlerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index aa297b5..4d0605f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -18,8 +18,8 @@
import android.animation.ValueAnimator
import android.graphics.Color
-import android.testing.AndroidTestingRunner
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.media.controls.ui.view.GutsViewHolder
@@ -44,7 +44,7 @@
private const val TARGET_COLOR = Color.BLUE
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class ColorSchemeTransitionTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
index bb95ba3..2499c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert.fail
@@ -36,7 +36,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MetadataAnimationHandlerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 8a6b272..4e14fec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -18,11 +18,11 @@
import android.animation.Animator
import android.animation.ObjectAnimator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.SeekBar
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
@@ -40,7 +40,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SeekBarObserverTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 791563a..2f95936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.media.controls.ui.controller
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -50,7 +50,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardMediaControllerTest : SysuiTestCase() {
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 a89139b..f7b3f2e 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
@@ -22,10 +22,10 @@
import android.database.ContentObserver
import android.os.LocaleList
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.util.MathUtils.abs
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
@@ -40,6 +40,7 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.MediaTestUtils
@@ -107,7 +108,7 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MediaCarouselControllerTest : SysuiTestCase() {
val kosmos = testKosmos()
@@ -158,6 +159,7 @@
testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
+ applicationScope = kosmos.applicationCoroutineScope,
context = context,
mediaControlPanelFactory = mediaControlPanelFactory,
visualStabilityProvider = visualStabilityProvider,
@@ -194,7 +196,7 @@
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
MediaPlayerData.clear()
verify(globalSettings)
- .registerContentObserver(
+ .registerContentObserverSync(
eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
capture(settingsObserverCaptor)
)
@@ -893,7 +895,10 @@
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
@@ -920,7 +925,10 @@
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 6d7976e..ecc456c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -45,7 +45,6 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.util.TypedValue
import android.view.View
@@ -60,6 +59,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.LiveData
import androidx.media.utils.MediaConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
@@ -137,7 +137,7 @@
private const val APP_NAME = "APP_NAME"
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MediaControlPanelTest : SysuiTestCase() {
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 20fb701..bba01bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -18,10 +18,10 @@
import android.graphics.Rect
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
@@ -81,7 +81,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MediaHierarchyManagerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index 80ebe56..00b9a46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -23,7 +23,6 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.RippleDrawable
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
@@ -35,6 +34,7 @@
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.LiveData
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.CachingIconView
import com.android.systemui.SysuiTestCase
@@ -72,7 +72,7 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MediaViewControllerTest : SysuiTestCase() {
private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
private val mediaHostStatesManager = MediaHostStatesManager()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
index 0319aaa..e87f176 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
@@ -21,8 +21,8 @@
import android.graphics.LightingColorFilter
import android.graphics.Paint
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.graphics.ColorUtils
import com.android.systemui.SysuiTestCase
@@ -40,7 +40,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SquigglyProgressTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 1208369..d073cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.media.controls.ui.view
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -38,7 +38,7 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MediaCarouselScrollHandlerTest : SysuiTestCase() {
private val carouselWidth = 1038
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
index d3c703c..cdb060c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
@@ -16,17 +16,17 @@
package com.android.systemui.media.controls.ui.view
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaViewHolderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e1c2d3f..4da7b2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -20,12 +20,12 @@
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.MotionEvent
import android.widget.SeekBar
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.TaskExecutor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.Classifier
@@ -53,7 +53,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class SeekBarViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
index 86f3062..bb9d20f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.media.controls.util
-import android.testing.AndroidTestingRunner
import android.util.Pair as APair
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MediaDataUtilsTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 45ae506..856bc0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -59,12 +59,12 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
import android.view.View;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -95,7 +95,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MediaOutputControllerTest extends SysuiTestCase {
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
index 1e8fbea..50ae25c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -27,8 +27,8 @@
import android.content.Intent;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.flags.Flags;
@@ -40,7 +40,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MediaOutputDialogReceiverTest extends SysuiTestCase {
private MediaOutputDialogReceiver mMediaOutputDialogReceiver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
index a828843..8e9a1f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -19,9 +19,9 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.widget.FrameLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MediaComplicationViewControllerTest extends SysuiTestCase {
@Mock
private MediaHost mMediaHost;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 8f8630e..a49819e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -25,8 +25,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MediaDreamSentinelTest extends SysuiTestCase {
@Mock
MediaDataManager mMediaDataManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 27f59d29..f1d833f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -23,13 +23,13 @@
import android.media.MediaRoute2Info
import android.os.Handler
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -59,7 +59,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaTttChipControllerReceiverTest : SysuiTestCase() {
private lateinit var controllerReceiver: MediaTttChipControllerReceiver
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 3be50b1..111b8d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -24,7 +24,6 @@
import android.os.PowerManager
import android.os.VibrationAttributes
import android.os.VibrationEffect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
@@ -32,6 +31,7 @@
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
@@ -74,7 +74,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaTttSenderCoordinatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
index da448aa..6238257 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -1,7 +1,7 @@
package com.android.systemui.mediaprojection
import android.media.projection.IMediaProjectionManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
@@ -13,7 +13,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaProjectionMetricsLoggerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 2536078..22bdfe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -2,7 +2,7 @@
import android.content.ComponentName
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
@@ -24,7 +24,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
index db36131..a73df07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
@@ -7,9 +7,9 @@
import android.graphics.Point
import android.graphics.Rect
import android.hardware.HardwareBuffer
-import android.testing.AndroidTestingRunner
import android.view.Surface
import android.window.TaskSnapshot
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -24,7 +24,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class ActivityTaskManagerThumbnailLoaderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
index fa1c8f8..a0cd835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
@@ -20,6 +20,7 @@
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.icons.FastBitmapDrawable
import com.android.systemui.SysuiTestCase
@@ -31,10 +32,9 @@
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BasicPackageManagerAppIconLoaderTest : SysuiTestCase() {
private val packageManagerWrapper: PackageManagerWrapper = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 6ac86f5..2f61579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -3,7 +3,7 @@
import android.app.ActivityManager.RecentTaskInfo
import android.content.pm.UserInfo
import android.os.UserManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.CLONED
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ShellRecentTaskListProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index e487780..b7fefc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.mediaprojection.data.repository
import android.os.Binder
-import android.testing.AndroidTestingRunner
import android.view.ContentRecordingSession
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -34,7 +34,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
index c63efa1..ea5603d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
@@ -25,10 +25,11 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
import org.mockito.ArgumentMatchers.any
abstract class BaseScreenCaptureDevicePolicyResolverTest(private val precondition: Preconditions) :
@@ -81,7 +82,7 @@
}
}
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
class IsAllowedScreenCaptureDevicePolicyResolverTest(
private val test: IsScreenCaptureAllowedTestCase
@@ -468,7 +469,7 @@
}
}
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest(
private val test: IsScreenCaptureCompletelyDisabledTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
index 16c92ec..8fe8878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.mediaprojection.taskswitcher
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_PSS_TASK_SWITCHER
import com.android.systemui.SysuiTestCase
@@ -27,7 +27,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class MediaProjectionTaskSwitcherCoreStartableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
index bda0e1e..d3ce871 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.mediaprojection.taskswitcher.data.repository
import android.os.Binder
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -32,7 +32,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ActivityTaskManagerTasksRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
index 33e65f26..7fe55b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.mediaprojection.taskswitcher.domain.interactor
import android.content.Intent
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +35,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TaskSwitchInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
index ad18099..7417dac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
@@ -18,7 +18,7 @@
import android.app.Notification
import android.app.NotificationManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
@@ -43,7 +43,7 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
index a468953..5bedc13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel
import android.content.Intent
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
index c06a28e..a3be9e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.model
import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeDisplayTracker
@@ -24,10 +25,9 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class SysUiStateExtTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
index f03f4f7..9a78bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -32,10 +33,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
@SmallTest
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(AndroidJUnit4.class)
public class SysUiStateTest extends SysuiTestCase {
private static final int FLAG_1 = 1;
private static final int FLAG_2 = 1 << 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5e14b1a..820c3de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -175,7 +175,7 @@
mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
mMainExecutor.runAllReady();
- mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
+ mSecureSettings.registerContentObserverForUserSync(SETTING, new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index db11c3e..196f654 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -19,6 +19,8 @@
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -28,11 +30,13 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.qs.QSLongPressEffect
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.qsTileFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -536,10 +540,30 @@
assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
}
+ @Test
+ @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+ fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
+ val tile = kosmos.qsTileFactory.createTile("Test Tile")
+ tileView.init(tile)
+
+ assertThat(tileView.isTileAddedToLongPress).isTrue()
+ assertThat(tileView.isExpandableAddedToLongPress).isTrue()
+ }
+
+ @Test
+ @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+ fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
+ val tile = kosmos.qsTileFactory.createTile("Test Tile")
+ tileView.init(tile)
+
+ assertThat(tileView.isTileAddedToLongPress).isFalse()
+ assertThat(tileView.isExpandableAddedToLongPress).isFalse()
+ }
+
class FakeTileView(
context: Context,
collapsed: Boolean,
- longPressEffect: QSLongPressEffect?,
+ private val longPressEffect: QSLongPressEffect?,
) : QSTileViewImpl(
ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
collapsed,
@@ -547,6 +571,11 @@
) {
var constantLongPressEffectDuration = 500
+ val isTileAddedToLongPress: Boolean
+ get() = longPressEffect?.qsTile != null
+ val isExpandableAddedToLongPress: Boolean
+ get() = longPressEffect?.expandable != null
+
override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
fun changeState(state: QSTile.State) {
handleStateChanged(state)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 5363c57..308b370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -312,9 +312,10 @@
info = new CarrierTextManager.CarrierTextCallbackInfo(
"",
new CharSequence[]{""},
- true,
+ /* anySimReady= */ true,
+ /* isInSatelliteMode= */ false,
new int[]{0},
- true /* airplaneMode */);
+ /* airplaneMode= */ true);
mCallback.updateCarrierInfo(info);
mTestableLooper.processAllMessages();
assertEquals(View.GONE, mShadeCarrierGroup.getNoSimTextView().getVisibility());
@@ -326,15 +327,59 @@
info = new CarrierTextManager.CarrierTextCallbackInfo(
"",
new CharSequence[]{FIRST_CARRIER_NAME, ""},
- true,
+ /* anySimReady= */ true,
+ /* isInSatelliteMode= */ false,
new int[]{0, 1},
- false /* airplaneMode */);
+ /* airplaneMode= */ false);
mCallback.updateCarrierInfo(info);
mTestableLooper.processAllMessages();
assertEquals(View.VISIBLE, mShadeCarrierGroupController.getShadeCarrierVisibility(0));
}
@Test
+ public void isInSatelliteMode_true_noSimViewShownWithText() {
+ CarrierTextManager.CarrierTextCallbackInfo
+ info = new CarrierTextManager.CarrierTextCallbackInfo(
+ "Satellite Mode Test",
+ new CharSequence[]{FIRST_CARRIER_NAME},
+ /* anySimReady= */ true,
+ /* isInSatelliteMode= */ true,
+ new int[]{1},
+ /* airplaneMode= */ false);
+
+ mCallback.updateCarrierInfo(info);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mShadeCarrierGroup.getNoSimTextView().getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mShadeCarrierGroup.getNoSimTextView().getText()).isEqualTo(
+ "Satellite Mode Test");
+
+ verify(mShadeCarrier1).setVisibility(View.GONE);
+ verify(mShadeCarrier2).setVisibility(View.GONE);
+ verify(mShadeCarrier3).setVisibility(View.GONE);
+ }
+
+ @Test
+ public void isInSatelliteMode_false_normalSimViewsShown() {
+ CarrierTextManager.CarrierTextCallbackInfo
+ info = new CarrierTextManager.CarrierTextCallbackInfo(
+ "Satellite Mode Test",
+ new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
+ /* anySimReady= */ true,
+ /* isInSatelliteMode= */ false,
+ new int[]{0, 1},
+ /* airplaneMode= */ false);
+
+ mCallback.updateCarrierInfo(info);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mShadeCarrierGroup.getNoSimTextView().getVisibility()).isEqualTo(View.GONE);
+
+ verify(mShadeCarrier1).setVisibility(View.VISIBLE);
+ verify(mShadeCarrier2).setVisibility(View.VISIBLE);
+ }
+
+ @Test
public void testListenerNotCalledOnRegistreation() {
mShadeCarrierGroupController
.setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
@@ -350,8 +395,7 @@
SINGLE_CARRIER_TEXT,
new CharSequence[]{SINGLE_CARRIER_TEXT},
true,
- new int[]{0},
- false /* airplaneMode */);
+ new int[]{0});
mCallback.updateCarrierInfo(info);
mTestableLooper.processAllMessages();
@@ -369,8 +413,7 @@
MULTI_CARRIER_TEXT,
new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
true,
- new int[]{0, 1},
- false /* airplaneMode */);
+ new int[]{0, 1});
mCallback.updateCarrierInfo(info);
mTestableLooper.processAllMessages();
@@ -387,16 +430,14 @@
SINGLE_CARRIER_TEXT,
new CharSequence[]{FIRST_CARRIER_NAME},
true,
- new int[]{0},
- false /* airplaneMode */);
+ new int[]{0});
CarrierTextManager.CarrierTextCallbackInfo
multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
MULTI_CARRIER_TEXT,
new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
true,
- new int[]{0, 1},
- false /* airplaneMode */);
+ new int[]{0, 1});
mCallback.updateCarrierInfo(singleCarrierInfo);
mTestableLooper.processAllMessages();
@@ -421,8 +462,7 @@
SINGLE_CARRIER_TEXT,
new CharSequence[]{FIRST_CARRIER_NAME},
true,
- new int[]{0},
- false /* airplaneMode */);
+ new int[]{0});
mCallback.updateCarrierInfo(singleCarrierInfo);
mTestableLooper.processAllMessages();
@@ -443,8 +483,7 @@
MULTI_CARRIER_TEXT,
new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
true,
- new int[]{0, 1},
- false /* airplaneMode */);
+ new int[]{0, 1});
mCallback.updateCarrierInfo(multiCarrierInfo);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 63ce233..068e166 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -32,6 +32,7 @@
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.provider.Settings
+import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
@@ -84,6 +85,7 @@
import java.util.concurrent.Executor
@SmallTest
+@RunWithLooper(setAsMainLooper = true)
class LockscreenSmartspaceControllerTest : SysuiTestCase() {
companion object {
const val SMARTSPACE_TIME_TOO_EARLY = 1000L
@@ -778,6 +780,7 @@
}
@Test
+ @RunWithLooper(setAsMainLooper = false)
fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
// GIVEN an uninitalized smartspaceView
// WHEN the device is provisioned
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 8012768..dc9c22f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -320,7 +320,7 @@
.thenReturn(1);
ArgumentCaptor<ContentObserver> contentObserverCaptor = ArgumentCaptor.forClass(
ContentObserver.class);
- verify(mSecureSettings).registerContentObserverForUser(eq(SHOW_NOTIFICATION_SNOOZE),
+ verify(mSecureSettings).registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE),
contentObserverCaptor.capture(), anyInt());
ContentObserver contentObserver = contentObserverCaptor.getValue();
contentObserver.onChange(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 7943872..2f77b33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -69,22 +69,21 @@
private val groupMembershipManager: GroupMembershipManager = mock()
private val section = NotifSection(mock(), 0)
- private val entry = NotificationEntryBuilder()
- .setSection(section)
- .setParent(GroupEntry.ROOT_ENTRY)
- .build()
+ private val entry =
+ NotificationEntryBuilder().setSection(section).setParent(GroupEntry.ROOT_ENTRY).build()
private lateinit var contentObserver: ContentObserver
- private val adjustmentProvider = NotifUiAdjustmentProvider(
- handler,
- secureSettings,
- lockscreenUserManager,
- sensitiveNotifProtectionController,
- sectionStyleProvider,
- userTracker,
- groupMembershipManager,
- )
+ private val adjustmentProvider =
+ NotifUiAdjustmentProvider(
+ handler,
+ secureSettings,
+ lockscreenUserManager,
+ sensitiveNotifProtectionController,
+ sectionStyleProvider,
+ userTracker,
+ groupMembershipManager,
+ )
@Before
fun setup() {
@@ -92,9 +91,8 @@
adjustmentProvider.addDirtyListener(dirtyListener)
verify(secureSettings).getIntForUser(eq(SHOW_NOTIFICATION_SNOOZE), any(), any())
contentObserver = withArgCaptor {
- verify(secureSettings).registerContentObserverForUser(
- eq(SHOW_NOTIFICATION_SNOOZE), capture(), any()
- )
+ verify(secureSettings)
+ .registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE), capture(), any())
}
verifyNoMoreInteractions(secureSettings, dirtyListener)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
index 352b79f..310fa67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
@@ -102,6 +102,7 @@
verify(userTracker).addCallback(any(), any())
verify(dumpManager).registerNormalDumpable(anyString(), eq(controller))
}
+
@Test
fun updateContentObserverRegistration_onUserChange_noSettingsListeners() {
verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
@@ -112,10 +113,11 @@
userCallback.onUserChanged(userId, context)
// Validate: Nothing to do, since we aren't monitoring settings
- verify(secureSettings, never()).unregisterContentObserver(any())
+ verify(secureSettings, never()).unregisterContentObserverSync(any())
verify(secureSettings, never())
- .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
}
+
@Test
fun updateContentObserverRegistration_onUserChange_withSettingsListeners() {
// When: someone is listening to a setting
@@ -129,9 +131,9 @@
userCallback.onUserChanged(userId, context)
// Validate: The tracker is unregistered and re-registered with the new user
- verify(secureSettings).unregisterContentObserver(any())
+ verify(secureSettings).unregisterContentObserverSync(any())
verify(secureSettings)
- .registerContentObserverForUser(eq(settingUri1), eq(false), any(), eq(userId))
+ .registerContentObserverForUserSync(eq(settingUri1), eq(false), any(), eq(userId))
}
@Test
@@ -140,7 +142,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -149,7 +151,7 @@
controller.addCallback(settingUri1, Mockito.mock(Listener::class.java))
verify(secureSettings)
- .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
}
@Test
@@ -158,7 +160,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -170,7 +172,7 @@
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri2),
eq(false),
any(),
@@ -186,7 +188,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -198,18 +200,18 @@
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(eq(settingUri2), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(eq(settingUri2), anyBoolean(), any(), anyInt())
clearInvocations(secureSettings)
controller.removeCallback(settingUri2, listenerSetting2)
testableLooper.processAllMessages()
- verify(secureSettings, never()).unregisterContentObserver(any())
+ verify(secureSettings, never()).unregisterContentObserverSync(any())
clearInvocations(secureSettings)
controller.removeCallback(settingUri1, listenerSetting1)
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
- verify(secureSettings).unregisterContentObserver(any())
+ verify(secureSettings).unregisterContentObserverSync(any())
}
@Test
@@ -253,7 +255,7 @@
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
any(Uri::class.java),
anyBoolean(),
capture(settingsObserverCaptor),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index eb2538e..7d586cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -131,8 +131,9 @@
mobileMappings,
fakeBroadcastDispatcher,
context,
- IMMEDIATE,
+ /* bgDispatcher = */ IMMEDIATE,
scope,
+ /* mainDispatcher = */ IMMEDIATE,
FakeAirplaneModeRepository(),
wifiRepository,
mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 96e599f..76982ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -141,8 +141,8 @@
private val wifiPickerTrackerCallback =
argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
- private val dispatcher = StandardTestDispatcher()
- private val testScope = TestScope(dispatcher)
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
private lateinit var underTest: MobileConnectionsRepositoryImpl
@@ -194,7 +194,7 @@
flags,
testScope.backgroundScope,
mainExecutor,
- dispatcher,
+ testDispatcher,
wifiPickerTrackerFactory,
wifiManager,
wifiLogBuffer,
@@ -216,7 +216,7 @@
fakeBroadcastDispatcher,
connectivityManager,
telephonyManager = telephonyManager,
- bgDispatcher = dispatcher,
+ bgDispatcher = testDispatcher,
logger = logger,
mobileMappingsProxy = mobileMappings,
scope = testScope.backgroundScope,
@@ -249,8 +249,9 @@
mobileMappings,
fakeBroadcastDispatcher,
context,
- dispatcher,
+ /* bgDispatcher = */ testDispatcher,
testScope.backgroundScope,
+ /* mainDispatcher = */ testDispatcher,
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
@@ -1225,8 +1226,9 @@
mobileMappings,
fakeBroadcastDispatcher,
context,
- dispatcher,
+ testDispatcher,
testScope.backgroundScope,
+ testDispatcher,
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 5ad88ad..d2c1dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -202,7 +202,7 @@
verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
verify(mDumpManager).registerDumpable(any(), any());
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index 99f6303..25a44e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -83,7 +83,7 @@
@Test
public void testRegisterContentObserver() {
- mFakeSettings.registerContentObserver("cat", mContentObserver);
+ mFakeSettings.registerContentObserverSync("cat", mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -93,7 +93,7 @@
@Test
public void testRegisterContentObserverAllUsers() {
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
mFakeSettings.putString("cat", "hat");
@@ -104,8 +104,8 @@
@Test
public void testUnregisterContentObserver() {
- mFakeSettings.registerContentObserver("cat", mContentObserver);
- mFakeSettings.unregisterContentObserver(mContentObserver);
+ mFakeSettings.registerContentObserverSync("cat", mContentObserver);
+ mFakeSettings.unregisterContentObserverSync(mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -115,9 +115,9 @@
@Test
public void testUnregisterContentObserverAllUsers() {
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
- mFakeSettings.unregisterContentObserver(mContentObserver);
+ mFakeSettings.unregisterContentObserverSync(mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -128,7 +128,7 @@
@Test
public void testContentObserverDispatchCorrectUser() {
int user = 10;
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index ab95707..eb11e38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -52,14 +52,14 @@
@Test
fun registerContentObserver_inputString_success() {
- mSettings.registerContentObserver(TEST_SETTING, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
}
@Test
fun registerContentObserver_inputString_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING,
notifyForDescendants = true,
mContentObserver
@@ -70,14 +70,14 @@
@Test
fun registerContentObserver_inputUri_success() {
- mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
}
@Test
fun registerContentObserver_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver
@@ -88,7 +88,7 @@
@Test
fun unregisterContentObserver() {
- mSettings.unregisterContentObserver(mContentObserver)
+ mSettings.unregisterContentObserverSync(mContentObserver)
verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 56328b9..38469ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -58,7 +58,7 @@
@Test
fun registerContentObserverForUser_inputString_success() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING,
mContentObserver,
mUserTracker.userId
@@ -74,7 +74,7 @@
@Test
fun registerContentObserverForUser_inputString_notifyForDescendants_true() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING,
notifyForDescendants = true,
mContentObserver,
@@ -91,7 +91,7 @@
@Test
fun registerContentObserverForUser_inputUri_success() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING_URI,
mContentObserver,
mUserTracker.userId
@@ -107,7 +107,7 @@
@Test
fun registerContentObserverForUser_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver,
@@ -124,14 +124,14 @@
@Test
fun registerContentObserver_inputUri_success() {
- mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver), eq(0))
}
@Test
fun registerContentObserver_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
index cd2710e..fb983f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.settings.userTracker
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.mockito.mock
@@ -29,6 +30,7 @@
CommunalSettingsInteractor(
bgScope = applicationCoroutineScope,
bgExecutor = fakeExecutor,
+ bgDispatcher = testDispatcher,
repository = communalSettingsRepository,
userInteractor = selectedUserInteractor,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 297d1d8..0a3a2ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.util.mockito.mock
@@ -25,7 +25,8 @@
val Kosmos.shadeLockscreenInteractor by
Kosmos.Fixture {
ShadeLockscreenInteractorImpl(
- scope = testScope,
+ applicationScope = applicationCoroutineScope,
+ backgroundScope = applicationCoroutineScope,
shadeInteractor = shadeInteractorImpl,
sceneInteractor = sceneInteractor,
lockIconViewController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index beabaf5..3a70cdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -44,7 +44,7 @@
}
@Override
- public void registerContentObserver(Uri uri, boolean notifyDescendants,
+ public void registerContentObserverSync(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver) {
List<ContentObserver> observers;
mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
@@ -53,7 +53,7 @@
}
@Override
- public void unregisterContentObserver(ContentObserver settingsObserver) {
+ public void unregisterContentObserverSync(ContentObserver settingsObserver) {
for (Map.Entry<String, List<ContentObserver>> entry :
mContentObserversAllUsers.entrySet()) {
entry.getValue().remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index a491886..cd219ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -66,7 +66,7 @@
}
@Override
- public void registerContentObserverForUser(Uri uri, boolean notifyDescendants,
+ public void registerContentObserverForUserSync(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver, int userHandle) {
List<ContentObserver> observers;
if (userHandle == UserHandle.USER_ALL) {
@@ -81,7 +81,7 @@
}
@Override
- public void unregisterContentObserver(ContentObserver settingsObserver) {
+ public void unregisterContentObserverSync(ContentObserver settingsObserver) {
for (SettingsKey key : mContentObservers.keySet()) {
List<ContentObserver> observers = mContentObservers.get(key);
observers.remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index 3f51a79..e2d414e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.domain.interactor
+import android.content.applicationContext
import com.android.systemui.bluetooth.bluetoothAdapter
import com.android.systemui.bluetooth.localBluetoothManager
import com.android.systemui.kosmos.Kosmos
@@ -27,6 +28,7 @@
val Kosmos.audioOutputInteractor by
Kosmos.Fixture {
AudioOutputInteractor(
+ applicationContext,
audioRepository,
audioModeInteractor,
testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
new file mode 100644
index 0000000..9f11822
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+import com.android.systemui.volume.mediaDeviceSessionInteractor
+import com.android.systemui.volume.mediaOutputInteractor
+
+val Kosmos.mediaOutputComponentInteractor by
+ Kosmos.Fixture {
+ MediaOutputComponentInteractor(
+ testScope.backgroundScope,
+ mediaDeviceSessionInteractor,
+ audioOutputInteractor,
+ audioModeInteractor,
+ mediaOutputInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
index 6d4576e..2cd6ff2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
@@ -20,11 +20,8 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
-import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
var Kosmos.mediaOutputViewModel by
Kosmos.Fixture {
@@ -32,10 +29,7 @@
applicationContext,
testScope.backgroundScope,
mediaOutputActionsInteractor,
- mediaDeviceSessionInteractor,
- audioOutputInteractor,
- audioModeInteractor,
- mediaOutputInteractor,
+ mediaOutputComponentInteractor,
uiEventLogger,
)
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 004f37c..2c4bc7c 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2514,7 +2514,7 @@
public Plane[] getPlanes() {
throwISEIfImageIsInvalid();
if (mPlanes == null) {
- int fenceFd = mParcelImage.fence != null ? mParcelImage.fence.getFd() : -1;
+ int fenceFd = mParcelImage.fence != null ? mParcelImage.fence.detachFd() : -1;
mGraphicBuffer = GraphicBuffer.createFromHardwareBuffer(mParcelImage.buffer);
mPlanes = ImageReader.initializeImagePlanes(mParcelImage.planeCount, mGraphicBuffer,
fenceFd, mParcelImage.format, mParcelImage.timestamp,
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 77decb6..7342b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1748,9 +1748,7 @@
}
public void resetLocked() {
- if (Flags.resettableDynamicProperties()) {
- mAccessibilityServiceInfo.resetDynamicallyConfigurableProperties();
- }
+ mAccessibilityServiceInfo.resetDynamicallyConfigurableProperties();
mSystemSupport.getKeyEventDispatcher().flush(this);
try {
// Clear the proxy in the other process so this
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fe08338..d09cb3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -5272,13 +5272,9 @@
//Clip to the window bounds.
Rect windowBounds = mTempRect1;
- if (Flags.focusClickPointWindowBoundsFromA11yWindowInfo()) {
- AccessibilityWindowInfo window = focus.getWindow();
- if (window != null) {
- window.getBoundsInScreen(windowBounds);
- }
- } else {
- getWindowBounds(focus.getWindowId(), windowBounds);
+ AccessibilityWindowInfo window = focus.getWindow();
+ if (window != null) {
+ window.getBoundsInScreen(windowBounds);
}
if (!boundsInScreenBeforeMagnification.intersect(windowBounds)) {
return false;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0d0c21d..30e4a3e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -34,6 +34,7 @@
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
@@ -334,6 +335,12 @@
enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
"get associations");
+ if (!checkCallerCanManageCompanionDevice(getContext())) {
+ // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to
+ // request the feature (also: the caller is the app itself).
+ enforceUsesCompanionDeviceFeature(getContext(), userId, packageName);
+ }
+
return mAssociationStore.getActiveAssociationsByPackage(userId, packageName);
}
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index 3fbd856..d09d7e6 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -347,8 +347,6 @@
* Set association tag.
*/
public void setAssociationTag(int associationId, String tag) {
- Slog.i(TAG, "Setting association tag=[" + tag + "] to id=[" + associationId + "]...");
-
AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
associationId);
association = (new AssociationInfo.Builder(association)).setTag(tag).build();
diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 757abd9..29e8095 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -18,7 +18,7 @@
import static com.android.server.companion.utils.MetricUtils.logCreateAssociation;
import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation;
-import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -457,10 +457,6 @@
/**
* Get association by id with caller checks.
- *
- * If the association is not found, an IllegalArgumentException would be thrown.
- *
- * If the caller can't access the association, a SecurityException would be thrown.
*/
@NonNull
public AssociationInfo getAssociationWithCallerChecks(int associationId) {
@@ -470,9 +466,13 @@
"getAssociationWithCallerChecks() Association id=[" + associationId
+ "] doesn't exist.");
}
- enforceCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
- association.getPackageName(), null);
- return association;
+ if (checkCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
+ association.getPackageName())) {
+ return association;
+ }
+
+ throw new IllegalArgumentException(
+ "The caller can't interact with the association id=[" + associationId + "].");
}
/**
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 6f0baef..8c1116b 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -98,6 +98,7 @@
Slog.i(TAG, "Disassociating id=[" + id + "]...");
final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id);
+
final int userId = association.getUserId();
final String packageName = association.getPackageName();
final String deviceProfile = association.getDeviceProfile();
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 00e049c..026d29c 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -122,6 +122,7 @@
*/
public boolean isPermissionTransferUserConsented(int associationId) {
mAssociationStore.getAssociationWithCallerChecks(associationId);
+
PermissionSyncRequest request = getPermissionSyncRequest(associationId);
if (request == null) {
return false;
@@ -146,12 +147,12 @@
return null;
}
- Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
- + "] associationId [" + associationId + "]");
-
final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
associationId);
+ Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
+ + "] associationId [" + associationId + "]");
+
// Create an internal intent to launch the user consent dialog
final Bundle extras = new Bundle();
PermissionSyncRequest request = new PermissionSyncRequest(associationId);
@@ -219,9 +220,7 @@
* Enable perm sync for the association
*/
public void enablePermissionsSync(int associationId) {
- AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
- associationId);
- int userId = association.getUserId();
+ int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
PermissionSyncRequest request = new PermissionSyncRequest(associationId);
request.setUserConsented(true);
mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -231,9 +230,7 @@
* Disable perm sync for the association
*/
public void disablePermissionsSync(int associationId) {
- AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
- associationId);
- int userId = association.getUserId();
+ int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
PermissionSyncRequest request = new PermissionSyncRequest(associationId);
request.setUserConsented(false);
mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -244,9 +241,8 @@
*/
@Nullable
public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
- AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
- associationId);
- int userId = association.getUserId();
+ int userId = mAssociationStore.getAssociationWithCallerChecks(associationId)
+ .getUserId();
List<SystemDataTransferRequest> requests =
mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
associationId);
@@ -263,9 +259,7 @@
*/
public void removePermissionSyncRequest(int associationId) {
Binder.withCleanCallingIdentity(() -> {
- AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
- associationId);
- int userId = association.getUserId();
+ int userId = mAssociationStore.getAssociationById(associationId).getUserId();
mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
});
}
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 796d285..f397814 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -149,6 +149,21 @@
}
/**
+ * Check if the caller is system UID or the provided user.
+ */
+ public static boolean checkCallerIsSystemOr(@UserIdInt int userId,
+ @NonNull String packageName) {
+ final int callingUid = getCallingUid();
+ if (callingUid == SYSTEM_UID) return true;
+
+ if (getCallingUserId() != userId) return false;
+
+ if (!checkPackage(callingUid, packageName)) return false;
+
+ return true;
+ }
+
+ /**
* Check if the calling user id matches the userId, and if the package belongs to
* the calling uid.
*/
@@ -169,30 +184,21 @@
}
/**
+ * Check if the caller holds the necessary permission to manage companion devices.
+ */
+ public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
+ if (getCallingUid() == SYSTEM_UID) return true;
+
+ return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
+ }
+
+ /**
* Require the caller to be able to manage the associations for the package.
*/
public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName,
@Nullable String actionDescription) {
- final int callingUid = getCallingUid();
-
- // If the caller is the system
- if (callingUid == SYSTEM_UID) {
- return;
- }
-
- // If caller can manage the package or has the permissions to manage companion devices
- boolean canInteractAcrossUsers = context.checkCallingPermission(INTERACT_ACROSS_USERS)
- == PERMISSION_GRANTED;
- boolean canManageCompanionDevices = context.checkCallingPermission(MANAGE_COMPANION_DEVICES)
- == PERMISSION_GRANTED;
- if (getCallingUserId() == userId) {
- if (checkPackage(callingUid, packageName) || canManageCompanionDevices) {
- return;
- }
- } else if (canInteractAcrossUsers && canManageCompanionDevices) {
- return;
- }
+ if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
+ "permissions to "
@@ -213,6 +219,25 @@
}
}
+ /**
+ * Check if the caller is either:
+ * <ul>
+ * <li> the package itself
+ * <li> the System ({@link android.os.Process#SYSTEM_UID})
+ * <li> holds {@link Manifest.permission#MANAGE_COMPANION_DEVICES} and, if belongs to a
+ * different user, also holds {@link Manifest.permission#INTERACT_ACROSS_USERS}.
+ * </ul>
+ * @return whether the caller is one of the above.
+ */
+ public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
+ @UserIdInt int userId, @NonNull String packageName) {
+ if (checkCallerIsSystemOr(userId, packageName)) return true;
+
+ if (!checkCallerCanInteractWithUserId(context, userId)) return false;
+
+ return checkCallerCanManageCompanionDevice(context);
+ }
+
private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) {
try {
return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index db4840d..211f952 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -83,8 +83,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
-import dalvik.annotation.optimization.NeverCompile;
-
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
@@ -100,6 +98,8 @@
import java.util.Random;
import java.util.Set;
+import dalvik.annotation.optimization.NeverCompile;
+
public final class CachedAppOptimizer {
// Flags stored in the DeviceConfig API.
@@ -2633,7 +2633,7 @@
public void binderError(int debugPid, ProcessRecord app, int code, int flags, int err) {
Slog.w(TAG_AM, "pid " + debugPid + " " + (app == null ? "null" : app.processName)
+ " sent binder code " + code + " with flags " + flags
- + " and got error " + err);
+ + " to frozen apps and got error " + err);
// Do nothing if the binder error callback is not enabled.
// That means the frozen apps in a wrong state will be killed when they are unfrozen later.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2184340..b9cdf27 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1005,7 +1005,7 @@
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
- pw.printf(" types=%08X", foregroundServiceType);
+ pw.printf(" types=0x%08X", foregroundServiceType);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
if (isShortFgs() && mShortFgsInfo != null) {
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 5690a9e..69bc66f 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -135,7 +135,7 @@
float[] alternativeRefreshRates,
@Display.HdrCapabilities.HdrType int[] supportedHdrTypes) {
return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate,
- vsyncRate, false, alternativeRefreshRates, supportedHdrTypes);
+ vsyncRate, /* isSynthetic= */ false, alternativeRefreshRates, supportedHdrTypes);
}
public interface Listener {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index baa154d..184ae41 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -47,6 +47,8 @@
private final BrightnessEvent mBrightnessEvent;
private final int mBrightnessAdjustmentFlag;
+ private final boolean mIsUserInitiatedChange;
+
private DisplayBrightnessState(Builder builder) {
mBrightness = builder.getBrightness();
mSdrBrightness = builder.getSdrBrightness();
@@ -60,6 +62,7 @@
mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
mBrightnessEvent = builder.getBrightnessEvent();
mBrightnessAdjustmentFlag = builder.getBrightnessAdjustmentFlag();
+ mIsUserInitiatedChange = builder.isUserInitiatedChange();
}
/**
@@ -148,6 +151,13 @@
return mBrightnessAdjustmentFlag;
}
+ /**
+ * Gets if the current brightness changes are because of a user initiated change
+ */
+ public boolean isUserInitiatedChange() {
+ return mIsUserInitiatedChange;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -168,6 +178,7 @@
stringBuilder.append("\n mBrightnessEvent:")
.append(Objects.toString(mBrightnessEvent, "null"));
stringBuilder.append("\n mBrightnessAdjustmentFlag:").append(mBrightnessAdjustmentFlag);
+ stringBuilder.append("\n mIsUserInitiatedChange:").append(mIsUserInitiatedChange);
return stringBuilder.toString();
}
@@ -199,7 +210,8 @@
&& mShouldUpdateScreenBrightnessSetting
== otherState.shouldUpdateScreenBrightnessSetting()
&& Objects.equals(mBrightnessEvent, otherState.getBrightnessEvent())
- && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag();
+ && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag()
+ && mIsUserInitiatedChange == otherState.isUserInitiatedChange();
}
@Override
@@ -207,7 +219,8 @@
return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
mCustomAnimationRate,
- mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag);
+ mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag,
+ mIsUserInitiatedChange);
}
/**
@@ -236,6 +249,8 @@
public int mBrightnessAdjustmentFlag = 0;
+ private boolean mIsUserInitiatedChange;
+
/**
* Create a builder starting with the values from the specified {@link
* DisplayBrightnessState}.
@@ -257,6 +272,7 @@
state.shouldUpdateScreenBrightnessSetting());
builder.setBrightnessEvent(state.getBrightnessEvent());
builder.setBrightnessAdjustmentFlag(state.getBrightnessAdjustmentFlag());
+ builder.setIsUserInitiatedChange(state.isUserInitiatedChange());
return builder;
}
@@ -464,5 +480,20 @@
mBrightnessAdjustmentFlag = brightnessAdjustmentFlag;
return this;
}
+
+ /**
+ * Gets if the current change is a user initiated change
+ */
+ public boolean isUserInitiatedChange() {
+ return mIsUserInitiatedChange;
+ }
+
+ /**
+ * This is used to set if the current change is a user initiated change
+ */
+ public Builder setIsUserInitiatedChange(boolean isUserInitiatedChange) {
+ mIsUserInitiatedChange = isUserInitiatedChange;
+ return this;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d4c0b01..5fd0253 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -49,6 +49,8 @@
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
+import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
+
import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -4972,8 +4974,9 @@
}
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
- final boolean ownContent = (displayDevice.getDisplayDeviceInfoLocked().flags
- & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
+ final boolean isRearDisplay = display.getDevicePositionLocked() == POSITION_REAR;
+ final boolean ownContent = ((displayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0) || isRearDisplay;
// If the display has enabled mirroring, but specified that it will be managed by
// WindowManager, return an invalid display id. This is to ensure we don't
// accidentally select the display id to mirror based on DM logic and instead allow
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b97053b..8d71c70 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1391,6 +1391,7 @@
// request changes.
final boolean wasShortTermModelActive =
mAutomaticBrightnessStrategy.isShortTermModelActive();
+ boolean userInitiatedChange = displayBrightnessState.isUserInitiatedChange();
boolean allowAutoBrightnessWhileDozing =
mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled()
@@ -1413,13 +1414,14 @@
mPowerRequest.policy,
mDisplayBrightnessController.getLastUserSetScreenBrightness(),
userSetBrightnessChanged);
+
+ // If the brightness is already set then it's been overridden by something other than
+ // the user, or is a temporary adjustment.
+ userInitiatedChange = (Float.isNaN(brightnessState))
+ && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
+ || userSetBrightnessChanged);
}
- // If the brightness is already set then it's been overridden by something other than the
- // user, or is a temporary adjustment.
- boolean userInitiatedChange = (Float.isNaN(brightnessState))
- && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
- || userSetBrightnessChanged);
final int autoBrightnessState = mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index aa17df6..d567331 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -600,6 +600,7 @@
private StrategyExecutionRequest constructStrategyExecutionRequest(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
float currentScreenBrightness = getCurrentBrightness();
- return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness);
+ return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
+ mUserSetScreenBrightnessUpdated);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 82c0bbf..304640b 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -29,10 +29,14 @@
private final float mCurrentScreenBrightness;
+ // Represents if the user set screen brightness was changed or not.
+ private boolean mUserSetBrightnessChanged;
+
public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
- float currentScreenBrightness) {
+ float currentScreenBrightness, boolean userSetBrightnessChanged) {
mDisplayPowerRequest = displayPowerRequest;
mCurrentScreenBrightness = currentScreenBrightness;
+ mUserSetBrightnessChanged = userSetBrightnessChanged;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -43,6 +47,10 @@
return mCurrentScreenBrightness;
}
+ public boolean isUserSetBrightnessChanged() {
+ return mUserSetBrightnessChanged;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategyExecutionRequest)) {
@@ -50,11 +58,13 @@
}
StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
- && mCurrentScreenBrightness == other.getCurrentScreenBrightness();
+ && mCurrentScreenBrightness == other.getCurrentScreenBrightness()
+ && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
}
@Override
public int hashCode() {
- return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness);
+ return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
+ mUserSetBrightnessChanged);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2907364..2b5241f 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -290,6 +290,8 @@
.setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
.setShouldUpdateScreenBrightnessSetting(
brightness != strategyExecutionRequest.getCurrentScreenBrightness())
+ .setIsUserInitiatedChange(getAutoBrightnessAdjustmentChanged()
+ || strategyExecutionRequest.isUserSetBrightnessChanged())
.build();
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 3463649a..0b92317 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -45,6 +45,7 @@
// The fallback brightness might change due to clamping. Make sure we tell the rest
// of the system by updating the setting
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(strategyExecutionRequest.isUserSetBrightnessChanged())
.build();
}
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 f923cbc9..f56d803 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -353,7 +353,7 @@
}
/**
- * @return Whether to ignore preferredRefreshRate app request or not
+ * @return Whether to ignore preferredRefreshRate app request conversion to display mode or not
*/
public boolean ignoreAppPreferredRefreshRateRequest() {
return mIgnoreAppPreferredRefreshRate.isEnabled();
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 76a2827..d519748 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1254,13 +1254,9 @@
* Responsible for keeping track of app requested refresh rates per display
*/
public final class AppRequestObserver {
- private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
- private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
private final boolean mIgnorePreferredRefreshRate;
AppRequestObserver(DisplayManagerFlags flags) {
- mAppRequestedModeByDisplay = new SparseArray<>();
- mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
mIgnorePreferredRefreshRate = flags.ignoreAppPreferredRefreshRateRequest();
}
@@ -1269,34 +1265,77 @@
*/
public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
+ Display.Mode requestedMode;
+ synchronized (mLock) {
+ requestedMode = findModeLocked(displayId, modeId, requestedRefreshRate);
+ }
- if (modeId == 0 && requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) {
+ Vote frameRateVote = getFrameRateVote(
+ requestedMinRefreshRateRange, requestedMaxRefreshRateRange);
+ Vote baseModeRefreshRateVote = getBaseModeVote(requestedMode, requestedRefreshRate);
+ Vote sizeVote = getSizeVote(requestedMode);
+
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
+ frameRateVote);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ baseModeRefreshRateVote);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+ }
+
+ private Display.Mode findModeLocked(int displayId, int modeId, float requestedRefreshRate) {
+ Display.Mode mode = null;
+ if (modeId != 0) {
+ mode = findAppModeByIdLocked(displayId, modeId);
+ } else if (requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) { // modeId == 0
// Scan supported modes returned to find a mode with the same
// size as the default display mode but with the specified refresh rate instead.
- Display.Mode mode = findDefaultModeByRefreshRate(displayId, requestedRefreshRate);
- if (mode != null) {
- modeId = mode.getModeId();
- } else {
+ mode = findDefaultModeByRefreshRateLocked(displayId, requestedRefreshRate);
+ if (mode == null) {
Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
+ requestedRefreshRate + " on Display: " + displayId);
}
}
+ return mode;
+ }
- synchronized (mLock) {
- setAppRequestedModeLocked(displayId, modeId);
- setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
- requestedMaxRefreshRateRange);
+ private Vote getFrameRateVote(float minRefreshRate, float maxRefreshRate) {
+ RefreshRateRange refreshRateRange = null;
+ if (minRefreshRate > 0 || maxRefreshRate > 0) {
+ float max = maxRefreshRate > 0
+ ? maxRefreshRate : Float.POSITIVE_INFINITY;
+ refreshRateRange = new RefreshRateRange(minRefreshRate, max);
+ if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
+ // minRefreshRate/maxRefreshRate were invalid
+ refreshRateRange = null;
+ }
}
+ return refreshRateRange != null
+ ? Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max) : null;
+ }
+
+ private Vote getSizeVote(@Nullable Display.Mode mode) {
+ return mode != null
+ ? Vote.forSize(mode.getPhysicalWidth(), mode.getPhysicalHeight()) : null;
+ }
+
+ private Vote getBaseModeVote(@Nullable Display.Mode mode, float requestedRefreshRate) {
+ Vote vote = null;
+ if (mode != null) {
+ if (mode.isSynthetic()) {
+ vote = Vote.forRequestedRefreshRate(mode.getRefreshRate());
+ } else {
+ vote = Vote.forBaseModeRefreshRate(mode.getRefreshRate());
+ }
+ } else if (requestedRefreshRate != 0f && mIgnorePreferredRefreshRate) {
+ vote = Vote.forRequestedRefreshRate(requestedRefreshRate);
+ } // !mIgnorePreferredRefreshRate case is handled by findModeLocked
+ return vote;
}
@Nullable
- private Display.Mode findDefaultModeByRefreshRate(int displayId, float refreshRate) {
- Display.Mode[] modes;
- Display.Mode defaultMode;
- synchronized (mLock) {
- modes = mAppSupportedModesByDisplay.get(displayId);
- defaultMode = mDefaultModeByDisplay.get(displayId);
- }
+ private Display.Mode findDefaultModeByRefreshRateLocked(int displayId, float refreshRate) {
+ Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
+ Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(defaultMode.getPhysicalWidth(),
defaultMode.getPhysicalHeight(), refreshRate)) {
@@ -1306,69 +1345,6 @@
return null;
}
- private void setAppRequestedModeLocked(int displayId, int modeId) {
- final Display.Mode requestedMode = findAppModeByIdLocked(displayId, modeId);
- if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
- return;
- }
- final Vote baseModeRefreshRateVote;
- final Vote sizeVote;
- if (requestedMode != null) {
- mAppRequestedModeByDisplay.put(displayId, requestedMode);
- sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
- requestedMode.getPhysicalHeight());
- if (requestedMode.isSynthetic()) {
- // TODO: for synthetic mode we should not limit frame rate, we must ensure
- // that frame rate is reachable within other Votes constraints
- baseModeRefreshRateVote = Vote.forRenderFrameRates(
- requestedMode.getRefreshRate(), requestedMode.getRefreshRate());
- } else {
- baseModeRefreshRateVote =
- Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
- }
- } else {
- mAppRequestedModeByDisplay.remove(displayId);
- baseModeRefreshRateVote = null;
- sizeVote = null;
- }
-
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
- baseModeRefreshRateVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
- }
-
- private void setAppPreferredRefreshRateRangeLocked(int displayId,
- float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
- final Vote vote;
-
- RefreshRateRange refreshRateRange = null;
- if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) {
- float min = requestedMinRefreshRateRange;
- float max = requestedMaxRefreshRateRange > 0
- ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY;
- refreshRateRange = new RefreshRateRange(min, max);
- if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
- // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid
- refreshRateRange = null;
- }
- }
-
- if (Objects.equals(refreshRateRange,
- mAppPreferredRefreshRateRangeByDisplay.get(displayId))) {
- return;
- }
-
- if (refreshRateRange != null) {
- mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
- vote = Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max);
- } else {
- mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
- vote = null;
- }
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
- vote);
- }
-
private Display.Mode findAppModeByIdLocked(int displayId, int modeId) {
Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
if (modes == null) {
@@ -1384,19 +1360,7 @@
private void dumpLocked(PrintWriter pw) {
pw.println(" AppRequestObserver");
- pw.println(" mAppRequestedModeByDisplay:");
- for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
- final int id = mAppRequestedModeByDisplay.keyAt(i);
- final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
- pw.println(" " + id + " -> " + mode);
- }
- pw.println(" mAppPreferredRefreshRateRangeByDisplay:");
- for (int i = 0; i < mAppPreferredRefreshRateRangeByDisplay.size(); i++) {
- final int id = mAppPreferredRefreshRateRangeByDisplay.keyAt(i);
- final RefreshRateRange refreshRateRange =
- mAppPreferredRefreshRateRangeByDisplay.valueAt(i);
- pw.println(" " + id + " -> " + refreshRateRange);
- }
+ pw.println(" mIgnorePreferredRefreshRate: " + mIgnorePreferredRefreshRate);
}
}
diff --git a/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
new file mode 100644
index 0000000..843cf88
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
@@ -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.server.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+class RequestedRefreshRateVote implements Vote {
+ final float mRefreshRate;
+
+ RequestedRefreshRateVote(float refreshRate) {
+ this.mRefreshRate = refreshRate;
+ }
+
+ @Override
+ public void updateSummary(@NonNull VoteSummary summary) {
+ summary.requestedRefreshRates.add(mRefreshRate);
+ }
+
+ @Override
+ public String toString() {
+ return "RequestedRefreshRateVote{ refreshRate=" + mRefreshRate + " }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RequestedRefreshRateVote that)) return false;
+ return Float.compare(mRefreshRate, that.mRefreshRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRefreshRate);
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
index 5b6bbc5..a83b939 100644
--- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
+++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
@@ -32,7 +32,9 @@
*/
public class SyntheticModeManager {
private static final float FLOAT_TOLERANCE = 0.01f;
- private static final float SYNTHETIC_MODE_HIGH_BOUNDARY = 60f + FLOAT_TOLERANCE;
+ private static final float SYNTHETIC_MODE_REFRESH_RATE = 60f;
+ private static final float SYNTHETIC_MODE_HIGH_BOUNDARY =
+ SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;
private final boolean mSynthetic60HzModesEnabled;
@@ -62,7 +64,7 @@
nextModeId = mode.getModeId();
}
- float divisor = mode.getVsyncRate() / 60f;
+ float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE;
boolean is60HzAchievable = Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE;
if (is60HzAchievable) {
sizes.put(new Size(mode.getPhysicalWidth(), mode.getPhysicalHeight()),
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 3e8b3c5..1ec469c 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -186,6 +186,10 @@
return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
+ static Vote forRequestedRefreshRate(float refreshRate) {
+ return new RequestedRefreshRateVote(refreshRate);
+ }
+
static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
if (supportedModes.isEmpty()) {
return null;
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index d4ce892..00a9226 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -23,7 +23,9 @@
import android.view.SurfaceControl;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
final class VoteSummary {
private static final float FLOAT_TOLERANCE = SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
@@ -38,7 +40,14 @@
public int minWidth;
public int minHeight;
public boolean disableRefreshRateSwitching;
+ /**
+ * available modes should have mode with specific refresh rate
+ */
public float appRequestBaseModeRefreshRate;
+ /**
+ * requestRefreshRate should be within [minRenderFrameRate, maxRenderFrameRate] range
+ */
+ public Set<Float> requestedRefreshRates = new HashSet<>();
@Nullable
public List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates;
@@ -329,6 +338,21 @@
}
return false;
}
+
+ for (Float requestedRefreshRate : requestedRefreshRates) {
+ if (requestedRefreshRate < minRenderFrameRate
+ || requestedRefreshRate > maxRenderFrameRate) {
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Requested refreshRate is outside frame rate range"
+ + ": requestedRefreshRates=" + requestedRefreshRates
+ + ", requestedRefreshRate=" + requestedRefreshRate
+ + ", minRenderFrameRate=" + minRenderFrameRate
+ + ", maxRenderFrameRate=" + maxRenderFrameRate);
+ }
+ return false;
+ }
+ }
+
return true;
}
@@ -370,6 +394,7 @@
minHeight = 0;
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
+ requestedRefreshRates.clear();
supportedRefreshRates = null;
supportedModeIds = null;
if (mLoggingEnabled) {
@@ -393,6 +418,7 @@
+ ", minHeight=" + minHeight
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate
+ + ", requestRefreshRates=" + requestedRefreshRates
+ ", supportedRefreshRates=" + supportedRefreshRates
+ ", supportedModeIds=" + supportedModeIds
+ ", mIsDisplayResolutionRangeVotingEnabled="
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index 79f1a9c..c19cb03 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -55,7 +55,6 @@
import java.util.List;
import java.util.Objects;
import java.util.OptionalInt;
-import java.util.function.IntConsumer;
// TODO(b/210039666): See if we can make this class thread-safe.
final class HandwritingModeController {
@@ -91,7 +90,6 @@
private boolean mDelegationConnectionlessFlow;
private Runnable mDelegationIdleTimeoutRunnable;
private Handler mDelegationIdleTimeoutHandler;
- private IntConsumer mPointerToolTypeConsumer;
private final Runnable mDiscardDelegationTextRunnable;
private HandwritingEventReceiverSurface mHandwritingSurface;
@@ -99,7 +97,7 @@
@AnyThread
HandwritingModeController(Context context, Looper uiThreadLooper,
- Runnable inkWindowInitRunnable, IntConsumer toolTypeConsumer,
+ Runnable inkWindowInitRunnable,
Runnable discardDelegationTextRunnable) {
mContext = context;
mLooper = uiThreadLooper;
@@ -109,7 +107,6 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mCurrentRequestId = 0;
mInkWindowInitRunnable = inkWindowInitRunnable;
- mPointerToolTypeConsumer = toolTypeConsumer;
mDiscardDelegationTextRunnable = discardDelegationTextRunnable;
}
@@ -389,16 +386,10 @@
"Input Event should not be processed when IME has the spy channel.");
}
- if (!(ev instanceof MotionEvent)) {
+ if (!(ev instanceof MotionEvent event)) {
Slog.wtf(TAG, "Received non-motion event in stylus monitor.");
return false;
}
- final MotionEvent event = (MotionEvent) ev;
- if (mPointerToolTypeConsumer != null && event.getAction() == MotionEvent.ACTION_DOWN) {
- int toolType = event.getToolType(event.getActionIndex());
- // notify IME of change in tool type.
- mPointerToolTypeConsumer.accept(toolType);
- }
if (!event.isStylusPointer()) {
return false;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index dace67f..f61ca61 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server.inputmethod;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -30,6 +32,9 @@
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
import java.util.Collections;
import java.util.List;
@@ -38,6 +43,16 @@
*/
public abstract class InputMethodManagerInternal {
/**
+ * Indicates that the method is guaranteed to not require {@link ImfLock}.
+ *
+ * <p>You can call this method without worrying about system_server lock layering.</p>
+ */
+ @Retention(SOURCE)
+ @Target({ElementType.METHOD})
+ public @interface ImfLockFree {
+ }
+
+ /**
* Listener for input method list changed events.
*/
public interface InputMethodListListener {
@@ -53,6 +68,7 @@
*
* @param interactive the interactive mode parameter
*/
+ @ImfLockFree
public abstract void setInteractive(boolean interactive);
/**
@@ -61,6 +77,7 @@
* @param reason the reason for hiding the current input method
* @param originatingDisplayId the display ID the request is originated
*/
+ @ImfLockFree
public abstract void hideAllInputMethods(@SoftInputShowHideReason int reason,
int originatingDisplayId);
@@ -143,6 +160,7 @@
*
* @param listener the listener to add
*/
+ @ImfLockFree
public abstract void registerInputMethodListListener(InputMethodListListener listener);
/**
@@ -178,6 +196,7 @@
*
* @param displayId the display hosting the IME window
*/
+ @ImfLockFree
public abstract void removeImeSurface(int displayId);
/**
@@ -188,12 +207,14 @@
* @param disableImeIcon indicates whether IME icon should be enabled or not
* @param displayId the display for which to update the IME window status
*/
+ @ImfLockFree
public abstract void updateImeWindowStatus(boolean disableImeIcon, int displayId);
/**
* Finish stylus handwriting by calling {@link InputMethodService#finishStylusHandwriting()} if
* there is an ongoing handwriting session.
*/
+ @ImfLockFree
public abstract void maybeFinishStylusHandwriting();
/**
@@ -240,10 +261,12 @@
*/
private static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
+ @ImfLockFree
@Override
public void setInteractive(boolean interactive) {
}
+ @ImfLockFree
@Override
public void hideAllInputMethods(@SoftInputShowHideReason int reason,
int originatingDisplayId) {
@@ -282,6 +305,7 @@
int deviceId, @Nullable String imeId) {
}
+ @ImfLockFree
@Override
public void registerInputMethodListListener(InputMethodListListener listener) {
}
@@ -300,10 +324,12 @@
public void onImeParentChanged(int displayId) {
}
+ @ImfLockFree
@Override
public void removeImeSurface(int displayId) {
}
+ @ImfLockFree
@Override
public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
}
@@ -318,6 +344,7 @@
@UserIdInt int userId) {
}
+ @ImfLockFree
@Override
public void maybeFinishStylusHandwriting() {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7513c40..d22438f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -205,7 +205,6 @@
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-import java.util.function.IntConsumer;
import java.util.function.IntFunction;
/**
@@ -1305,12 +1304,9 @@
com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
mNonPreemptibleInputMethods = mRes.getStringArray(
com.android.internal.R.array.config_nonPreemptibleInputMethods);
- IntConsumer toolTypeConsumer =
- Flags.useHandwritingListenerForTooltype()
- ? toolType -> onUpdateEditorToolType(toolType) : null;
Runnable discardDelegationTextRunnable = () -> discardHandwritingDelegationText();
mHwController = new HandwritingModeController(mContext, thread.getLooper(),
- new InkWindowInitializer(), toolTypeConsumer, discardDelegationTextRunnable);
+ new InkWindowInitializer(), discardDelegationTextRunnable);
registerDeviceListenerAndCheckStylusSupport();
}
}
@@ -3416,8 +3412,10 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
mCurStatsToken = null;
- if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
- curMethod.updateEditorToolType(lastClickToolType);
+ if (Flags.useHandwritingListenerForTooltype()) {
+ maybeReportToolType();
+ } else if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+ onUpdateEditorToolType(lastClickToolType);
}
mVisibilityApplier.performShowIme(windowToken, statsToken,
mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
@@ -3431,6 +3429,29 @@
return false;
}
+ @GuardedBy("ImfLock.class")
+ private void maybeReportToolType() {
+ int lastDeviceId = mInputManagerInternal.getLastUsedInputDeviceId();
+ final InputManager im = mContext.getSystemService(InputManager.class);
+ if (im == null) {
+ return;
+ }
+ InputDevice device = im.getInputDevice(lastDeviceId);
+ if (device == null) {
+ return;
+ }
+ int toolType;
+ if (isStylusDevice(device)) {
+ toolType = MotionEvent.TOOL_TYPE_STYLUS;
+ } else if (isFingerDevice(device)) {
+ toolType = MotionEvent.TOOL_TYPE_FINGER;
+ } else {
+ // other toolTypes are irrelevant and reported as unknown.
+ toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+ }
+ onUpdateEditorToolType(toolType);
+ }
+
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@@ -4285,6 +4306,10 @@
|| inputDevice.supportsSource(InputDevice.SOURCE_BLUETOOTH_STYLUS);
}
+ private static boolean isFingerDevice(InputDevice inputDevice) {
+ return inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+ }
+
@GuardedBy("ImfLock.class")
private boolean hasSupportedStylusLocked() {
return mStylusIds != null && mStylusIds.size() != 0;
@@ -5475,12 +5500,14 @@
private final class LocalServiceImpl extends InputMethodManagerInternal {
+ @ImfLockFree
@Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0).sendToTarget();
}
+ @ImfLockFree
@Override
public void hideAllInputMethods(@SoftInputShowHideReason int reason,
int originatingDisplayId) {
@@ -5566,6 +5593,7 @@
}
}
+ @ImfLockFree
@Override
public void registerInputMethodListListener(InputMethodListListener listener) {
mInputMethodListListeners.addIfAbsent(listener);
@@ -5613,11 +5641,13 @@
}
}
+ @ImfLockFree
@Override
public void removeImeSurface(int displayId) {
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
}
+ @ImfLockFree
@Override
public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
@@ -5695,6 +5725,7 @@
}
}
+ @ImfLockFree
@Override
public void maybeFinishStylusHandwriting() {
mHandler.removeMessages(MSG_FINISH_HANDWRITING);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b3ab229..ae3d36a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -593,7 +593,7 @@
public RebootEscrowManager getRebootEscrowManager(RebootEscrowManager.Callbacks callbacks,
LockSettingsStorage storage) {
return new RebootEscrowManager(mContext, callbacks, storage,
- getHandler(getServiceThread()));
+ getHandler(getServiceThread()), getUserManagerInternal());
}
public int binderGetCallingUid() {
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index e1cd2c5..d0b8990 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -51,16 +51,20 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.pm.UserManagerInternal;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.Set;
import javax.crypto.SecretKey;
@@ -138,6 +142,7 @@
ERROR_KEYSTORE_FAILURE,
ERROR_NO_NETWORK,
ERROR_TIMEOUT_EXHAUSTED,
+ ERROR_NO_REBOOT_ESCROW_DATA,
})
@Retention(RetentionPolicy.SOURCE)
@interface RebootEscrowErrorCode {
@@ -153,6 +158,7 @@
static final int ERROR_KEYSTORE_FAILURE = 7;
static final int ERROR_NO_NETWORK = 8;
static final int ERROR_TIMEOUT_EXHAUSTED = 9;
+ static final int ERROR_NO_REBOOT_ESCROW_DATA = 10;
private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
@@ -222,11 +228,16 @@
private final RebootEscrowKeyStoreManager mKeyStoreManager;
private final LockSettingsStorage mStorage;
private RebootEscrowProviderInterface mRebootEscrowProvider;
+ private final UserManagerInternal mUserManagerInternal;
- Injector(Context context, LockSettingsStorage storage) {
+ Injector(
+ Context context,
+ LockSettingsStorage storage,
+ UserManagerInternal userManagerInternal) {
mContext = context;
mStorage = storage;
mKeyStoreManager = new RebootEscrowKeyStoreManager();
+ mUserManagerInternal = userManagerInternal;
}
private RebootEscrowProviderInterface createRebootEscrowProvider() {
@@ -326,6 +337,10 @@
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
+ public UserManagerInternal getUserManagerInternal() {
+ return mUserManagerInternal;
+ }
+
public RebootEscrowKeyStoreManager getKeyStoreManager() {
return mKeyStoreManager;
}
@@ -402,8 +417,8 @@
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage,
- Handler handler) {
- this(new Injector(context, storage), callbacks, storage, handler);
+ Handler handler, UserManagerInternal userManagerInternal) {
+ this(new Injector(context, storage, userManagerInternal), callbacks, storage, handler);
}
@VisibleForTesting
@@ -451,18 +466,50 @@
onEscrowRestoreComplete(false, attemptCount, retryHandler);
}
- void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
- List<UserInfo> users = mUserManager.getUsers();
- List<UserInfo> rebootEscrowUsers = new ArrayList<>();
+ private List<UserInfo> getUsersToUnlock(List<UserInfo> users) {
+ // System user must be unlocked to unlock any other user
+ if (mCallbacks.isUserSecure(USER_SYSTEM) && !mStorage.hasRebootEscrow(USER_SYSTEM)) {
+ Slog.i(TAG, "No reboot escrow data found for system user");
+ return Collections.emptyList();
+ }
+
+ Set<Integer> noEscrowDataUsers = new HashSet<>();
for (UserInfo user : users) {
- if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) {
- rebootEscrowUsers.add(user);
+ if (mCallbacks.isUserSecure(user.id)
+ && !mStorage.hasRebootEscrow(user.id)) {
+ Slog.d(TAG, "No reboot escrow data found for user " + user);
+ noEscrowDataUsers.add(user.id);
}
}
+ List<UserInfo> rebootEscrowUsers = new ArrayList<>();
+ for (UserInfo user : users) {
+ // No lskf, no need to unlock.
+ if (!mCallbacks.isUserSecure(user.id)) {
+ continue;
+ }
+ // Don't unlock if user or user's parent does not have reboot data
+ int userId = user.id;
+ if (noEscrowDataUsers.contains(userId)
+ || noEscrowDataUsers.contains(
+ mInjector.getUserManagerInternal().getProfileParentId(userId))) {
+ continue;
+ }
+ rebootEscrowUsers.add(user);
+ }
+ return rebootEscrowUsers;
+ }
+
+ void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
+ List<UserInfo> users = mUserManager.getUsers();
+ List<UserInfo> rebootEscrowUsers = getUsersToUnlock(users);
+
if (rebootEscrowUsers.isEmpty()) {
Slog.i(TAG, "No reboot escrow data found for users,"
+ " skipping loading escrow data");
+ setLoadEscrowDataErrorCode(ERROR_NO_REBOOT_ESCROW_DATA, retryHandler);
+ reportMetricOnRestoreComplete(
+ /* success= */ false, /* attemptCount= */ 1, retryHandler);
clearMetricsStorage();
return;
}
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 27b8574..d060f8f2 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -32,12 +32,13 @@
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
/** Default implementation for {@link DeviceEffectsApplier}. */
class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
-
+ private static final String TAG = "DeviceEffectsApplier";
private static final String SUPPRESS_AMBIENT_DISPLAY_TOKEN =
"DefaultDeviceEffectsApplier:SuppressAmbientDisplay";
private static final int SATURATION_LEVEL_GRAYSCALE = 0;
@@ -75,28 +76,44 @@
Binder.withCleanCallingIdentity(() -> {
if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
!= effects.shouldSuppressAmbientDisplay()) {
- mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
- effects.shouldSuppressAmbientDisplay());
+ try {
+ mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
+ effects.shouldSuppressAmbientDisplay());
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change AOD override", e);
+ }
}
if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
if (mColorDisplayManager != null) {
- mColorDisplayManager.setSaturationLevel(
- effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
- : SATURATION_LEVEL_FULL_COLOR);
+ try {
+ mColorDisplayManager.setSaturationLevel(
+ effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
+ : SATURATION_LEVEL_FULL_COLOR);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change grayscale override", e);
+ }
}
}
if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
if (mWallpaperManager != null) {
- mWallpaperManager.setWallpaperDimAmount(
- effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
- : WALLPAPER_DIM_AMOUNT_NORMAL);
+ try {
+ mWallpaperManager.setWallpaperDimAmount(
+ effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
+ : WALLPAPER_DIM_AMOUNT_NORMAL);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change wallpaper override", e);
+ }
}
}
if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) {
- updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
+ try {
+ updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change dark theme override", e);
+ }
}
});
@@ -131,9 +148,13 @@
private void updateNightModeImmediately(boolean useNightMode) {
Binder.withCleanCallingIdentity(() -> {
- mUiModeManager.setAttentionModeThemeOverlay(
- useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
- : MODE_ATTENTION_THEME_OVERLAY_OFF);
+ try {
+ mUiModeManager.setAttentionModeThemeOverlay(
+ useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
+ : MODE_ATTENTION_THEME_OVERLAY_OFF);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not change wallpaper override", e);
+ }
});
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 3949dfe..1a8e44b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1626,8 +1626,8 @@
ApplicationInfo appInfo = null;
try {
- appInfo = mContext.getPackageManager().getApplicationInfo(
- name.getPackageName(), 0);
+ appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+ name.getPackageName(), 0, userid);
} catch (NameNotFoundException e) {
// Ignore if the package doesn't exist we won't be able to bind to the service.
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ae29e1b..454bd20 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -149,6 +149,8 @@
private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
+ private static final int MAX_ICON_RESOURCE_NAME_LENGTH = 1000;
+
/**
* Send new activation AutomaticZenRule statuses to apps with a min target SDK version
*/
@@ -2645,7 +2647,13 @@
requireNonNull(packageName);
try {
final Resources res = mPm.getResourcesForApplication(packageName);
- return res.getResourceName(resId);
+ String resourceName = res.getResourceName(resId);
+ if (resourceName != null && resourceName.length() > MAX_ICON_RESOURCE_NAME_LENGTH) {
+ Slog.e(TAG, "Resource name for ID=" + resId + " in package " + packageName
+ + " is too long (" + resourceName.length() + "); ignoring it");
+ return null;
+ }
+ return resourceName;
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
Slog.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+ ". Resource IDs may change when the application is upgraded, and the system"
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ac754b3..679ab65 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4057,9 +4057,6 @@
return;
}
- // Log the metrics when the component state is changed.
- PackageMetrics.reportComponentStateChanged(computer, componentStateMetricsList, userId);
-
if (isSynchronous) {
flushPackageRestrictionsAsUserInternalLocked(userId);
} else {
@@ -4079,6 +4076,10 @@
}
}
+ // Log the metrics when the component state is changed.
+ PackageMetrics.reportComponentStateChanged(snapshotComputer(), componentStateMetricsList,
+ userId);
+
final long callingId = Binder.clearCallingIdentity();
try {
final Computer newSnapshot = snapshotComputer();
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f4b61e7..04db3e8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -275,6 +275,10 @@
return KeyStoreAuthorization.getInstance();
}
+ AlarmManager getAlarmManager() {
+ return mContext.getSystemService(AlarmManager.class);
+ }
+
Looper getLooper() {
return Looper.myLooper();
}
@@ -293,7 +297,7 @@
mLockPatternUtils = injector.getLockPatternUtils();
mKeyStoreAuthorization = injector.getKeyStoreAuthorization();
mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = injector.getAlarmManager();
}
@Override
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 207707e..ac2c886 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -28,6 +28,7 @@
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.os.Process.INVALID_PID;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.ROOT_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
@@ -385,6 +386,10 @@
return BackgroundStartPrivileges.NONE;
case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
// no explicit choice by the app - let us decide what to do
+ if (callingUid == ROOT_UID || callingUid == SYSTEM_UID) {
+ // root and system must always opt in explicitly
+ return BackgroundStartPrivileges.NONE;
+ }
if (callingPackage != null) {
// determine based on the calling/creating package
boolean changeEnabled = CompatChanges.isChangeEnabled(
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d4bbe84..61022cc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1346,10 +1346,13 @@
// In a multi-resumed environment, like in a freeform device, the top
// activity can be resumed, but it might not be the focused app.
- // Set focused app when top activity is resumed
- if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null
- && taskDisplayArea.mDisplayContent.mFocusedApp != next) {
- taskDisplayArea.mDisplayContent.setFocusedApp(next);
+ // Set focused app when top activity is resumed. However, we shouldn't do it for a
+ // same task because it can break focused state. (e.g. activity embedding)
+ if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null) {
+ final ActivityRecord focusedApp = taskDisplayArea.mDisplayContent.mFocusedApp;
+ if (focusedApp == null || focusedApp.getTask() != next.getTask()) {
+ taskDisplayArea.mDisplayContent.setFocusedApp(next);
+ }
}
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ "resumed %s", next);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 7483b43..c7fd979 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -202,9 +202,9 @@
private String getDefaultSmsPackage() {
//TODO(b/319449037): Unflag the following change.
if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {
- return SmsApplication.getDefaultSmsApplicationAsUser(
- mContext, /*updateIfNeeded=*/ false, mContext.getUser())
- .getPackageName();
+ ComponentName defaultSmsApp = SmsApplication.getDefaultSmsApplicationAsUser(
+ mContext, /*updateIfNeeded=*/ false, mContext.getUser());
+ return defaultSmsApp != null ? defaultSmsApp.getPackageName() : null;
} else {
return Telephony.Sms.getDefaultSmsPackage(mContext);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 927df8b..cfe4e17 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -761,9 +761,6 @@
}
}
- private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
- private long mBinderCallbackLast = -1;
-
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
@@ -968,14 +965,6 @@
Binder.setTransactionCallback(new IBinderCallback() {
@Override
public void onTransactionError(int pid, int code, int flags, int err) {
-
- final long now = SystemClock.uptimeMillis();
- if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE_MS) {
- Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback.");
- return;
- }
- mBinderCallbackLast = now;
- Slog.wtfStack(TAG, "Binder Transaction Error");
mActivityManagerService.frozenBinderTransactionDetected(pid, code, flags, err);
}
});
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index b0c7073..3bdcd9b 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -18,6 +18,7 @@
import android.app.AppOpsManager
import android.companion.virtual.VirtualDeviceManager
+import android.os.Binder
import android.os.Handler
import android.os.UserHandle
import android.permission.PermissionManager
@@ -250,7 +251,9 @@
) {
Slog.w(
LOG_TAG,
- "Cannot set UID mode for runtime permission app op, uid = $uid," +
+ "Cannot set UID mode for runtime permission app op, " +
+ " callingUid = ${Binder.getCallingUid()}, " +
+ " uid = $uid," +
" code = ${AppOpsManager.opToName(code)}," +
" mode = ${AppOpsManager.modeToName(mode)}",
RuntimeException()
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index 8db896b..bba4c8d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -58,6 +58,7 @@
.setShouldUseAutoBrightness(shouldUseAutoBrightness)
.setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting)
.setBrightnessAdjustmentFlag(brightnessAdjustmentFlag)
+ .setIsUserInitiatedChange(true)
.build();
assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
@@ -109,7 +110,9 @@
.append("\n mBrightnessEvent:")
.append(Objects.toString(displayBrightnessState.getBrightnessEvent(), "null"))
.append("\n mBrightnessAdjustmentFlag:")
- .append(displayBrightnessState.getBrightnessAdjustmentFlag());
+ .append(displayBrightnessState.getBrightnessAdjustmentFlag())
+ .append("\n mIsUserInitiatedChange:")
+ .append(displayBrightnessState.isUserInitiatedChange());
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c5105e7..323ef6a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -121,7 +121,8 @@
any(StrategySelectionRequest.class))).thenReturn(displayBrightnessStrategy);
mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState);
verify(displayBrightnessStrategy).updateBrightness(
- eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS)));
+ eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
+ /* userSetBrightnessChanged= */ false)));
assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
displayBrightnessStrategy);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 7a6a911..4d5ff55 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -115,7 +115,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mAutoBrightnessFallbackStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index afb5a5c..8a33f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -513,9 +513,11 @@
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO)
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
- .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -560,9 +562,11 @@
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO_TEMP)
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
- .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 47f1746..3534325 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mBoostBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 9246780..bd6d8be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mDozeBrightnessModeStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4767ae..4cae35d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -57,10 +57,12 @@
.setSdrBrightness(currentBrightness)
.setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mFallbackBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+ new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
+ /* userSetBrightnessChanged= */ true));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 682c9cc..fdaf8f6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mFollowerBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index ccf6309..e3c2760 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mOffloadBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
.getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index 8e7b463..ebe407b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mOverrideBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index e799d0e..4bad569 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mScreenOffBrightnessModeStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index aaada41..5410a20 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mTemporaryBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index cf6146f..34c6ba9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -117,11 +117,11 @@
BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), RenderVote(60f, 90f)),
PREFERRED_REFRESH_RATE(false, 0, 60f, 0f, 0f,
BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
- PREFERRED_REFRESH_RATE_IGNORED(true, 0, 60f, 0f, 0f,
- null, null, null),
+ PREFERRED_REFRESH_RATE_IGNORE_BASE_MODE_CONVERSION(true, 0, 60f, 0f, 0f,
+ RequestedRefreshRateVote(60f), null, null),
PREFERRED_REFRESH_RATE_INVALID(false, 0, 25f, 0f, 0f,
null, null, null),
SYNTHETIC_MODE(false, 99, 0f, 0f, 0f,
- RenderVote(45f, 45f), SizeVote(1000, 1000, 1000, 1000), null),
+ RequestedRefreshRateVote(45f), SizeVote(1000, 1000, 1000, 1000), null),
}
}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
new file mode 100644
index 0000000..dbe9e4a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RequestedRefreshRateVoteTest {
+
+ @Test
+ fun `updates requestedRefreshRates`() {
+ val refreshRate = 90f
+ val vote = RequestedRefreshRateVote(refreshRate)
+ val summary = createVotesSummary()
+
+ vote.updateSummary(summary)
+
+ assertThat(summary.requestedRefreshRates).hasSize(1)
+ assertThat(summary.requestedRefreshRates).contains(refreshRate)
+ }
+
+ @Test
+ fun `updates requestedRefreshRates with multiple refresh rates`() {
+ val refreshRate1 = 90f
+ val vote1 = RequestedRefreshRateVote(refreshRate1)
+
+ val refreshRate2 = 60f
+ val vote2 = RequestedRefreshRateVote(refreshRate2)
+
+ val summary = createVotesSummary()
+
+ vote1.updateSummary(summary)
+ vote2.updateSummary(summary)
+
+ assertThat(summary.requestedRefreshRates).hasSize(2)
+ assertThat(summary.requestedRefreshRates).containsExactly(refreshRate1, refreshRate2)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index 5da1bb6..dd5e1be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -152,13 +152,51 @@
assertThat(result.map { it.modeId }).containsExactlyElementsIn(testCase.expectedModeIds)
}
+
+ @Test
+ fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(30f, 90f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).isEmpty()
+ }
+
+ @Test
+ fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(60f, 240f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).isEmpty()
+ }
+
+ @Test
+ fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(60f, 90f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).hasSize(1)
+ }
}
+
+
private fun createMode(modeId: Int, refreshRate: Float, vsyncRate: Float): Display.Mode {
return Display.Mode(modeId, 600, 800, refreshRate, vsyncRate, false,
FloatArray(0), IntArray(0))
}
-private fun createSummary(supportedModesVoteEnabled: Boolean): VoteSummary {
+private fun createSummary(supportedModesVoteEnabled: Boolean = false): VoteSummary {
val summary = createVotesSummary(supportedModesVoteEnabled = supportedModesVoteEnabled)
summary.width = 600
summary.height = 800
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 0532e04..2a67029 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.trust;
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
@@ -26,12 +28,22 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import static java.util.Collections.singleton;
+
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
@@ -65,15 +77,22 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.security.KeyStoreAuthorization;
+import android.service.trust.GrantTrustResult;
+import android.service.trust.ITrustAgentService;
+import android.service.trust.ITrustAgentServiceCallback;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
+import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.test.core.app.ApplicationProvider;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.StrongAuthFlags;
+import com.android.internal.widget.LockSettingsInternal;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -81,15 +100,19 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class TrustManagerServiceTest {
@@ -115,21 +138,28 @@
private static final int PROFILE_USER_ID = 70;
private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };
+ private static final long RENEWABLE_TRUST_DURATION = 10000L;
+ private static final String GRANT_TRUST_MESSAGE = "granted";
+ private static final String TAG = "TrustManagerServiceTest";
private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
+ private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>();
private @Mock ActivityManager mActivityManager;
+ private @Mock AlarmManager mAlarmManager;
private @Mock BiometricManager mBiometricManager;
private @Mock DevicePolicyManager mDevicePolicyManager;
private @Mock FaceManager mFaceManager;
private @Mock FingerprintManager mFingerprintManager;
private @Mock KeyStoreAuthorization mKeyStoreAuthorization;
private @Mock LockPatternUtils mLockPatternUtils;
+ private @Mock LockSettingsInternal mLockSettingsInternal;
private @Mock PackageManager mPackageManager;
private @Mock UserManager mUserManager;
private @Mock IWindowManager mWindowManager;
+ private @Mock ITrustListener mTrustListener;
private HandlerThread mHandlerThread;
private TrustManagerService mService;
@@ -158,6 +188,9 @@
return null;
}).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));
+ LocalServices.removeServiceForTest(LockSettingsInternal.class);
+ LocalServices.addService(LockSettingsInternal.class, mLockSettingsInternal);
+
ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
@@ -176,6 +209,7 @@
when(mWindowManager.isKeyguardLocked()).thenReturn(true);
mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
+ mMockContext.addMockSystemService(AlarmManager.class, mAlarmManager);
mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
mMockContext.addMockSystemService(FaceManager.class, mFaceManager);
mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
@@ -197,6 +231,7 @@
verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
+ mTrustManager.registerTrustListener(mTrustListener);
}
private class MockInjector extends TrustManagerService.Injector {
@@ -215,6 +250,11 @@
}
@Override
+ AlarmManager getAlarmManager() {
+ return mAlarmManager;
+ }
+
+ @Override
Looper getLooper() {
return mHandlerThread.getLooper();
}
@@ -367,12 +407,10 @@
@Test
public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
- final ITrustListener trustListener = mock(ITrustListener.class);
- mTrustManager.registerTrustListener(trustListener);
mService.waitForIdle();
mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
mService.waitForIdle();
- verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
+ verify(mTrustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
}
// Tests that when the device is locked for a managed profile with a *unified* challenge, the
@@ -416,6 +454,169 @@
}
@Test
+ public void testSuccessfulUnlock_bindsTrustAgent() throws Exception {
+ when(mUserManager.getAliveUsers())
+ .thenReturn(List.of(new UserInfo(TEST_USER_ID, "test", UserInfo.FLAG_FULL)));
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ ITrustAgentService trustAgentService =
+ setUpTrustAgentWithStrongAuthRequired(
+ trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+ attemptSuccessfulUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ assertThat(getCallback(trustAgentService)).isNotNull();
+ }
+
+ @Test
+ public void testSuccessfulUnlock_notifiesTrustAgent() throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ ITrustAgentService trustAgentService =
+ setUpTrustAgentWithStrongAuthRequired(
+ trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+ attemptSuccessfulUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ verify(trustAgentService).onUnlockAttempt(/* successful= */ true);
+ }
+
+ @Test
+ public void testSuccessfulUnlock_notifiesTrustListenerOfChangeInManagedTrust()
+ throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+ mService.waitForIdle();
+ Mockito.reset(mTrustListener);
+
+ attemptSuccessfulUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ verify(mTrustListener).onTrustManagedChanged(false, TEST_USER_ID);
+ }
+
+ @Test
+ @Ignore("TODO: b/340891566 - Trustagent always refreshes trustable timer for user 0 on unlock")
+ public void testSuccessfulUnlock_refreshesTrustableTimers() throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ ITrustAgentService trustAgent =
+ setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+ setUpRenewableTrust(trustAgent);
+
+ attemptSuccessfulUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ // Idle and hard timeout alarms for first renewable trust granted
+ // Idle timeout alarm refresh for second renewable trust granted
+ // Idle and hard timeout alarms refresh for last report
+ verify(mAlarmManager, times(3))
+ .setExact(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ anyLong(),
+ anyString(),
+ any(AlarmManager.OnAlarmListener.class),
+ any(Handler.class));
+ }
+
+ @Test
+ public void testFailedUnlock_doesNotBindTrustAgent() throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ ITrustAgentService trustAgentService =
+ setUpTrustAgentWithStrongAuthRequired(
+ trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+ attemptFailedUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ verify(trustAgentService, never()).setCallback(any());
+ }
+
+ @Test
+ public void testFailedUnlock_notifiesTrustAgent() throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ ITrustAgentService trustAgentService =
+ setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+
+ attemptFailedUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ verify(trustAgentService).onUnlockAttempt(/* successful= */ false);
+ }
+
+ @Test
+ public void testFailedUnlock_doesNotNotifyTrustListenerOfChangeInManagedTrust()
+ throws Exception {
+ ComponentName trustAgentName =
+ ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+ setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+ Mockito.reset(mTrustListener);
+
+ attemptFailedUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+
+ verify(mTrustListener, never()).onTrustManagedChanged(anyBoolean(), anyInt());
+ }
+
+ private void setUpRenewableTrust(ITrustAgentService trustAgent) throws RemoteException {
+ ITrustAgentServiceCallback callback = getCallback(trustAgent);
+ callback.setManagingTrust(true);
+ mService.waitForIdle();
+ attemptSuccessfulUnlock(TEST_USER_ID);
+ mService.waitForIdle();
+ when(mWindowManager.isKeyguardLocked()).thenReturn(false);
+ grantRenewableTrust(callback);
+ }
+
+ private ITrustAgentService setUpTrustAgentWithStrongAuthRequired(
+ ComponentName agentName, @StrongAuthFlags int strongAuthFlags) throws Exception {
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(TEST_USER_ID);
+ addTrustAgent(agentName, true);
+ mLockPatternUtils.setKnownTrustAgents(singleton(agentName), TEST_USER_ID);
+ mLockPatternUtils.setEnabledTrustAgents(singleton(agentName), TEST_USER_ID);
+ when(mUserManager.isUserUnlockingOrUnlocked(TEST_USER_ID)).thenReturn(true);
+ setupStrongAuthTracker(strongAuthFlags, false);
+ mService.waitForIdle();
+ return getOrCreateMockTrustAgent(agentName);
+ }
+
+ private void attemptSuccessfulUnlock(int userId) throws RemoteException {
+ mTrustManager.reportUnlockAttempt(/* successful= */ true, userId);
+ }
+
+ private void attemptFailedUnlock(int userId) throws RemoteException {
+ mTrustManager.reportUnlockAttempt(/* successful= */ false, userId);
+ }
+
+ private void grantRenewableTrust(ITrustAgentServiceCallback callback) throws RemoteException {
+ Log.i(TAG, "Granting trust");
+ AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
+ callback.grantTrust(
+ GRANT_TRUST_MESSAGE,
+ RENEWABLE_TRUST_DURATION,
+ FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
+ future);
+ mService.waitForIdle();
+ }
+
+ /**
+ * Retrieve the ITrustAgentServiceCallback attached to a TrustAgentService after it has been
+ * bound to by the TrustManagerService. Will fail if no binding was established.
+ */
+ private ITrustAgentServiceCallback getCallback(ITrustAgentService trustAgentService)
+ throws RemoteException {
+ ArgumentCaptor<ITrustAgentServiceCallback> callbackCaptor =
+ ArgumentCaptor.forClass(ITrustAgentServiceCallback.class);
+ verify(trustAgentService).setCallback(callbackCaptor.capture());
+ return callbackCaptor.getValue();
+ }
+
+ @Test
@RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
throws Exception {
@@ -637,6 +838,20 @@
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = serviceInfo;
mTrustAgentResolveInfoList.add(resolveInfo);
+ ITrustAgentService.Stub mockService = getOrCreateMockTrustAgent(agentComponentName);
+ mMockContext.addMockService(agentComponentName, mockService);
+ mMockTrustAgents.put(agentComponentName, mockService);
+ }
+
+ private ITrustAgentService.Stub getOrCreateMockTrustAgent(ComponentName agentComponentName) {
+ return mMockTrustAgents.computeIfAbsent(
+ agentComponentName,
+ (componentName) -> {
+ ITrustAgentService.Stub mockTrustAgent = mock(ITrustAgentService.Stub.class);
+ when(mockTrustAgent.queryLocalInterface(anyString()))
+ .thenReturn(mockTrustAgent);
+ return mockTrustAgent;
+ });
}
private void bootService() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index c4946f0..8914696 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -432,7 +432,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_RESETTABLE_DYNAMIC_PROPERTIES)
public void binderDied_resetA11yServiceInfo() {
final int flag = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
setServiceBinding(COMPONENT_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 64e6236..17b499e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -19,6 +19,7 @@
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.NO_PROFILE_GROUP_ID;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY;
@@ -32,6 +33,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyByte;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -65,6 +67,7 @@
import com.android.internal.widget.RebootEscrowListener;
import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+import com.android.server.pm.UserManagerInternal;
import org.junit.Before;
import org.junit.Test;
@@ -107,6 +110,7 @@
private Context mContext;
private UserManager mUserManager;
+ private UserManagerInternal mUserManagerInternal;
private RebootEscrowManager.Callbacks mCallbacks;
private IRebootEscrow mRebootEscrow;
private ResumeOnRebootServiceConnection mServiceConnection;
@@ -126,13 +130,15 @@
long getCurrentTimeMillis();
void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
- int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
+ int escrowDurationInSeconds, int vbmetaDigestStatus,
+ int durationSinceBootComplete);
}
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
private final UserManager mUserManager;
+ private final UserManagerInternal mUserManagerInternal;
private final MockableRebootEscrowInjected mInjected;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
private boolean mServerBased;
@@ -141,12 +147,16 @@
private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer;
private boolean mWaitForInternet;
- MockInjector(Context context, UserManager userManager,
+ MockInjector(
+ Context context,
+ UserManager userManager,
+ UserManagerInternal userManagerInternal,
IRebootEscrow rebootEscrow,
RebootEscrowKeyStoreManager keyStoreManager,
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
- super(context, storage);
+ // TODO: change this
+ super(context, storage, userManagerInternal);
mRebootEscrow = rebootEscrow;
mServerBased = false;
mWaitForInternet = false;
@@ -159,16 +169,20 @@
};
mDefaultRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
mUserManager = userManager;
+ mUserManagerInternal = userManagerInternal;
mKeyStoreManager = keyStoreManager;
mInjected = injected;
}
- MockInjector(Context context, UserManager userManager,
+ MockInjector(
+ Context context,
+ UserManager userManager,
+ UserManagerInternal userManagerInternal,
ResumeOnRebootServiceConnection serviceConnection,
RebootEscrowKeyStoreManager keyStoreManager,
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
- super(context, storage);
+ super(context, storage, userManagerInternal);
mRebootEscrow = null;
mServerBased = true;
mWaitForInternet = false;
@@ -187,6 +201,7 @@
mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(
storage, injector);
mUserManager = userManager;
+ mUserManagerInternal = userManagerInternal;
mKeyStoreManager = keyStoreManager;
mInjected = injected;
}
@@ -202,6 +217,11 @@
}
@Override
+ public UserManagerInternal getUserManagerInternal() {
+ return mUserManagerInternal;
+ }
+
+ @Override
public boolean serverBasedResumeOnReboot() {
return mServerBased;
}
@@ -289,8 +309,8 @@
@Override
public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
- int escrowDurationInSeconds, int vbmetaDigestStatus,
- int durationSinceBootComplete) {
+ int escrowDurationInSeconds, int vbmetaDigestStatus,
+ int durationSinceBootComplete) {
mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
@@ -301,6 +321,7 @@
public void setUp_baseServices() throws Exception {
mContext = new ContextWrapper(InstrumentationRegistry.getContext());
mUserManager = mock(UserManager.class);
+ mUserManagerInternal = mock(UserManagerInternal.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
@@ -314,28 +335,43 @@
new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
ArrayList<UserInfo> users = new ArrayList<>();
- users.add(new UserInfo(PRIMARY_USER_ID, "primary", FLAG_PRIMARY));
- users.add(new UserInfo(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE));
- users.add(new UserInfo(NONSECURE_SECONDARY_USER_ID, "non-secure", FLAG_FULL));
- users.add(new UserInfo(SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL));
+ users.add(createUser(PRIMARY_USER_ID, "primary", FLAG_PRIMARY, PRIMARY_USER_ID));
+ users.add(createUser(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE, PRIMARY_USER_ID));
+ users.add(
+ createUser(
+ NONSECURE_SECONDARY_USER_ID, "non-secure", FLAG_FULL, NO_PROFILE_GROUP_ID));
+ users.add(createUser(SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL, NO_PROFILE_GROUP_ID));
when(mUserManager.getUsers()).thenReturn(users);
when(mCallbacks.isUserSecure(PRIMARY_USER_ID)).thenReturn(true);
when(mCallbacks.isUserSecure(WORK_PROFILE_USER_ID)).thenReturn(true);
when(mCallbacks.isUserSecure(NONSECURE_SECONDARY_USER_ID)).thenReturn(false);
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
- mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
- mKeyStoreManager, mStorage, mInjected);
+ mMockInjector =
+ new MockInjector(
+ mContext,
+ mUserManager,
+ mUserManagerInternal,
+ mRebootEscrow,
+ mKeyStoreManager,
+ mStorage,
+ mInjected);
HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
thread.start();
mHandler = new Handler(thread.getLooper());
mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage, mHandler);
-
}
private void setServerBasedRebootEscrowProvider() throws Exception {
- mMockInjector = new MockInjector(mContext, mUserManager, mServiceConnection,
- mKeyStoreManager, mStorage, mInjected);
+ mMockInjector =
+ new MockInjector(
+ mContext,
+ mUserManager,
+ mUserManagerInternal,
+ mServiceConnection,
+ mKeyStoreManager,
+ mStorage,
+ mInjected);
mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage, mHandler);
}
@@ -352,6 +388,12 @@
waitForHandler();
}
+ private UserInfo createUser(int id, String name, int flag, int profileGroupId) {
+ UserInfo user = new UserInfo(id, name, flag);
+ when(mUserManagerInternal.getProfileParentId(eq(id))).thenReturn(profileGroupId);
+ return user;
+ }
+
@Test
public void prepareRebootEscrow_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
@@ -559,6 +601,172 @@
}
@Test
+ public void loadRebootEscrowDataIfAvailable_noDataPrimaryUser_Failure() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+
+ // escrow secondary user, don't escrow primary user
+ callToRebootEscrowIfNeededAndWait(SECURE_SECONDARY_USER_ID);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+ doNothing()
+ .when(mInjected)
+ .reportMetric(
+ metricsSuccessCaptor.capture(),
+ metricsErrorCodeCaptor.capture(),
+ eq(2) /* Server based */,
+ eq(1) /* attempt count */,
+ anyInt(),
+ eq(0) /* vbmeta status */,
+ anyInt());
+ mService.loadRebootEscrowDataIfAvailable(null);
+ verify(mServiceConnection, never()).unwrap(any(), anyLong());
+ verify(mCallbacks, never()).onRebootEscrowRestored(anyByte(), any(), anyInt());
+ assertFalse(metricsSuccessCaptor.getValue());
+ assertEquals(
+ Integer.valueOf(RebootEscrowManager.ERROR_NO_REBOOT_ESCROW_DATA),
+ metricsErrorCodeCaptor.getValue());
+ }
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_noDataSecondaryUser_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+
+ // Setup work profile with secondary user as parent.
+ ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(createUser(PRIMARY_USER_ID, "primary", FLAG_PRIMARY, NO_PROFILE_GROUP_ID));
+ users.add(createUser(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE, SECURE_SECONDARY_USER_ID));
+ users.add(
+ createUser(
+ SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL, SECURE_SECONDARY_USER_ID));
+ when(mUserManager.getUsers()).thenReturn(users);
+
+ // escrow primary user and work profile, don't escrow secondary user
+ callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+ callToRebootEscrowIfNeededAndWait(WORK_PROFILE_USER_ID);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrow(WORK_PROFILE_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing()
+ .when(mInjected)
+ .reportMetric(
+ metricsSuccessCaptor.capture(),
+ eq(0) /* error code */,
+ eq(2) /* Server based */,
+ eq(1) /* attempt count */,
+ anyInt(),
+ eq(0) /* vbmeta status */,
+ anyInt());
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ mService.loadRebootEscrowDataIfAvailable(null);
+
+ verify(mServiceConnection).unwrap(any(), anyLong());
+ verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(PRIMARY_USER_ID));
+ verify(mCallbacks, never())
+ .onRebootEscrowRestored(anyByte(), any(), eq(SECURE_SECONDARY_USER_ID));
+ verify(mCallbacks, never())
+ .onRebootEscrowRestored(anyByte(), any(), eq(WORK_PROFILE_USER_ID));
+ verify(mCallbacks, never())
+ .onRebootEscrowRestored(anyByte(), any(), eq(NONSECURE_SECONDARY_USER_ID));
+ assertTrue(metricsSuccessCaptor.getValue());
+ }
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_noDataWorkProfile_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+
+ // escrow primary user and secondary user, don't escrow work profile
+ callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+ callToRebootEscrowIfNeededAndWait(SECURE_SECONDARY_USER_ID);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(WORK_PROFILE_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing()
+ .when(mInjected)
+ .reportMetric(
+ metricsSuccessCaptor.capture(),
+ eq(0) /* error code */,
+ eq(2) /* Server based */,
+ eq(1) /* attempt count */,
+ anyInt(),
+ eq(0) /* vbmeta status */,
+ anyInt());
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ mService.loadRebootEscrowDataIfAvailable(null);
+
+ verify(mServiceConnection).unwrap(any(), anyLong());
+ verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(PRIMARY_USER_ID));
+ verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(SECURE_SECONDARY_USER_ID));
+ verify(mCallbacks, never())
+ .onRebootEscrowRestored(anyByte(), any(), eq(WORK_PROFILE_USER_ID));
+ verify(mCallbacks, never())
+ .onRebootEscrowRestored(anyByte(), any(), eq(NONSECURE_SECONDARY_USER_ID));
+ assertTrue(metricsSuccessCaptor.getValue());
+ }
+
+ @Test
public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
setServerBasedRebootEscrowProvider();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index bfbc81c..4bea95f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -25,10 +25,12 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -322,4 +324,26 @@
argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
anyInt());
}
+
+ @Test
+ public void apply_servicesThrow_noCrash() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ doThrow(new RuntimeException()).when(mPowerManager)
+ .suppressAmbientDisplay(anyString(), anyBoolean());
+ doThrow(new RuntimeException()).when(mColorDisplayManager).setSaturationLevel(anyInt());
+ doThrow(new RuntimeException()).when(mWallpaperManager).setWallpaperDimAmount(anyFloat());
+ doThrow(new RuntimeException()).when(mUiModeManager).setAttentionModeThemeOverlay(anyInt());
+ mApplier = new DefaultDeviceEffectsApplier(mContext);
+
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
+ .build();
+ mApplier.apply(effects, UPDATE_ORIGIN_USER);
+
+ // No crashes
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index e4188b0..72ace84 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -6168,6 +6168,23 @@
.isEqualTo(previousManualZenPolicy);
}
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void addRule_iconIdWithResourceNameTooLong_ignoresIcon() {
+ int resourceId = 999;
+ String veryLongResourceName = "com.android.server.notification:drawable/"
+ + "omg_this_is_one_long_resource_name".repeat(100);
+ when(mResources.getResourceName(resourceId)).thenReturn(veryLongResourceName);
+ when(mResources.getIdentifier(veryLongResourceName, null, null)).thenReturn(resourceId);
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(),
+ UPDATE_ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+ assertThat(storedRule.getIconResId()).isEqualTo(0);
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a85d809..c0cbdc3 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -31,6 +31,7 @@
"androidx.test.runner",
"androidx.test.uiautomator_uiautomator",
"compatibility-device-util-axt",
+ "cts-input-lib",
"flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 4893d14..6b95f5c 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -21,21 +21,23 @@
import android.app.ActivityManager
import android.app.ApplicationExitInfo
+import android.content.Context
import android.graphics.Rect
+import android.hardware.display.DisplayManager
import android.os.Build
import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
import android.testing.PollingCheck
-import android.view.InputDevice
-import android.view.MotionEvent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
+import com.android.cts.input.UinputTouchScreen
+
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -150,6 +152,18 @@
assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
}
+ private fun clickOnObject(obj: UiObject2) {
+ val displayManager =
+ instrumentation.context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ val display = displayManager.getDisplay(obj.getDisplayId())
+ val touchScreen = UinputTouchScreen(instrumentation, display)
+
+ val rect: Rect = obj.visibleBounds
+ val pointer = touchScreen.touchDown(rect.centerX(), rect.centerY())
+ pointer.lift()
+ touchScreen.close()
+ }
+
private fun triggerAnr() {
startUnresponsiveActivity()
val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
@@ -160,13 +174,7 @@
return
}
- val rect: Rect = obj.visibleBounds
- val downTime = SystemClock.uptimeMillis()
- val downEvent = MotionEvent.obtain(downTime, downTime,
- MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
- downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
-
- instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+ clickOnObject(obj)
SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
}