Merge changes I85431728,I803587d4,Iaba879e2 into main
* changes:
Add hashCode to TvStreamConfig
Suppress MissingGetterMatchingBuilder warning
Suppress UserHandleName warning
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 412f2b7..11da20a 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -284,6 +284,8 @@
"100-framework-minus-apex.ravenwood",
"200-kxml2-android",
+ "ravenwood-runtime-common-ravenwood",
+
"android.test.mock.ravenwood",
"ravenwood-helper-runtime",
"hoststubgen-helper-runtime.ravenwood",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 515ddc8..459c286 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -19,13 +19,16 @@
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;
@@ -80,7 +83,7 @@
private void prepareForNextRun() {
SystemClock.sleep(COOL_OFF_PERIOD_MS);
- ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
+ waitCoolDownPeriod();
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
@@ -102,7 +105,7 @@
* to avoid unnecessary waiting.
*/
public void resumeTiming() {
- ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
+ waitCoolDownPeriod();
resumeTimer();
}
@@ -162,4 +165,56 @@
}
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 762e2af..98ab0c2 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -188,21 +188,6 @@
}
}
- /** 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 {
@@ -239,7 +224,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -254,7 +238,6 @@
mRunner.pauseTiming();
final int userId = createUserNoFlags();
- waitForBroadcastIdle();
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -309,9 +292,6 @@
preStartUser(userId, numberOfIterationsToSkip);
- waitForBroadcastIdle();
- waitCoolDownPeriod();
-
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -353,9 +333,6 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitForBroadcastIdle();
- waitCoolDownPeriod();
-
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -420,7 +397,6 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitCoolDownPeriod();
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -454,7 +430,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -466,6 +441,7 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
+
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -479,27 +455,6 @@
}
}
- /** 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 {
@@ -507,6 +462,7 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
+
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -536,7 +492,6 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -562,7 +517,6 @@
/* useStaticWallpaper */true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -606,7 +560,6 @@
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -614,7 +567,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
- waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -631,7 +583,6 @@
/* useStaticWallpaper */ true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
- waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -639,7 +590,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
- waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -675,13 +625,11 @@
@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();
@@ -703,7 +651,7 @@
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
- waitForBroadcastIdle();
+ mRunner.waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -726,7 +674,7 @@
final int startUser = ActivityManager.getCurrentUser();
final int userId = createUserNoFlags();
- waitCoolDownPeriod();
+ mRunner.waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -752,7 +700,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- waitForBroadcastIdle();
+ mRunner.waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -781,7 +729,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- waitCoolDownPeriod();
+ mRunner.waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -827,7 +775,6 @@
Log.d(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -868,7 +815,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -913,7 +859,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
removeUser(userId);
@@ -965,7 +910,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1030,7 +974,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1071,7 +1014,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1124,7 +1066,6 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
- waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1164,7 +1105,6 @@
runThenWaitForBroadcasts(userId, () -> {
startUserInBackgroundAndWaitForUnlock(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- waitCoolDownPeriod();
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -1280,6 +1220,7 @@
* 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);
@@ -1296,7 +1237,7 @@
*/
private void stopUserAfterWaitingForBroadcastIdle(int userId)
throws RemoteException {
- waitForBroadcastIdle();
+ mRunner.waitCoolDownPeriod();
stopUser(userId);
}
@@ -1438,6 +1379,8 @@
*/
private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
String... actions) {
+ mRunner.waitCoolDownPeriod();
+
final String unreceivedAction =
mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);
@@ -1538,28 +1481,4 @@
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 76c1ed6..7dbf270 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 = 10_100L;
+ private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
private long mBinderCallbackLast = -1;
/**
@@ -7551,12 +7551,13 @@
@Override
public void onTransactionError(int pid, int code, int flags, int err) {
final long now = SystemClock.uptimeMillis();
- if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE) {
+ if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE_MS) {
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/Notification.java b/core/java/android/app/Notification.java
index dfae48b..4c839f1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5637,6 +5637,10 @@
contentView.setDrawableTint(R.id.profile_badge, false,
getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
}
+ contentView.setContentDescription(
+ R.id.profile_badge,
+ mContext.getSystemService(UserManager.class)
+ .getProfileAccessibilityString(mContext.getUserId()));
}
}
@@ -8924,10 +8928,9 @@
sender = stripStyling(sender);
}
- final boolean showOnlySenderName = showOnlySenderName();
-
final CharSequence title;
- boolean isConversationTitleAvailable = !showOnlySenderName && conversationTitle != null;
+ final boolean isConversationTitleAvailable = showConversationTitle()
+ && conversationTitle != null;
if (isConversationTitleAvailable) {
title = conversationTitle;
} else {
@@ -8947,10 +8950,10 @@
}
}
- /** developer settings to always show sender name */
- private boolean showOnlySenderName() {
+ /** (b/342370742) Developer settings to show conversation title. */
+ private boolean showConversationTitle() {
return SystemProperties.getBoolean(
- "persist.compact_heads_up_notification.show_only_sender_name",
+ "persist.compact_heads_up_notification.show_conversation_title_for_group",
false);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 193c524..3f6c81b 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -695,8 +695,8 @@
* {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
*
* @see #getVibrationEffect()
- * @see Vibrator#areEffectsSupported(int...)
- * @see Vibrator#arePrimitivesSupported(int...)
+ * @see android.os.Vibrator#areEffectsSupported(int...)
+ * @see android.os.Vibrator#arePrimitivesSupported(int...)
*/
@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
public void setVibrationEffect(@Nullable VibrationEffect effect) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c529f7d..44444b5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5842,6 +5842,24 @@
* with {@link #PASSWORD_QUALITY_UNSPECIFIED} on that instance prior to setting complexity
* requirement for the managed profile.
*
+ * Starting from {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, after the password
+ * requirement has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
+ * Bundle, TargetUser, PolicyUpdateResult)} will notify the admin on whether the policy was
+ * successfully set or not. This callback will contain:
+ * <ul>
+ * <li> The policy identifier {@link DevicePolicyIdentifiers#PASSWORD_COMPLEXITY_POLICY}
+ * <li> The {@link TargetUser} that this policy relates to
+ * <li> The {@link PolicyUpdateResult}, which will be
+ * {@link PolicyUpdateResult#RESULT_POLICY_SET} if the policy was successfully set or the
+ * reason the policy failed to be set
+ * e.g. {@link PolicyUpdateResult#RESULT_FAILURE_CONFLICTING_ADMIN_POLICY})
+ * </ul>
+ * If there has been a change to the policy,
+ * {@link PolicyUpdateReceiver#onPolicyChanged(Context, String, Bundle, TargetUser,
+ * PolicyUpdateResult)} will notify the admin of this change. This callback will contain the
+ * same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
+ * will contain the reason why the policy changed.
+ *
* @throws SecurityException if the calling application is not a device owner or a profile
* owner.
* @throws IllegalArgumentException if the complexity level is not one of the four above.
@@ -5849,6 +5867,7 @@
* are password requirements specified using {@link #setPasswordQuality(ComponentName, int)}
* on the parent {@code DevicePolicyManager} instance.
*/
+ @SupportsCoexistence
@RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, conditional = true)
public void setRequiredPasswordComplexity(@PasswordComplexity int passwordComplexity) {
if (mService == null) {
@@ -5880,6 +5899,7 @@
* owner.
*/
@PasswordComplexity
+ @SupportsCoexistence
@RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, conditional = true)
public int getRequiredPasswordComplexity() {
if (mService == null) {
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 86b4180..7d5806a 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -328,6 +328,16 @@
}
flag {
+ name: "unmanaged_mode_migration"
+ namespace: "enterprise"
+ description: "Migrate APIs for unmanaged mode"
+ bug: "335624297"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "headless_single_user_fixes"
namespace: "enterprise"
description: "Various fixes for headless single user mode"
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 6ceae17..55c3bb60 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -166,4 +166,11 @@
namespace: "systemui"
description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications"
bug: "336229954"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "remove_remote_views"
+ namespace: "systemui"
+ description: "Removes all custom views"
+ bug: "342602960"
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 70c25ea..740f593 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -41,4 +41,5 @@
void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
boolean isActiveUnlockRunning(int userId);
+ boolean isInSignificantPlace();
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 23b2ea4..88d4d69 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -299,6 +299,20 @@
}
}
+ /**
+ * Returns true if the device is currently in a significant place, and false in all other
+ * circumstances.
+ *
+ * @hide
+ */
+ public boolean isInSignificantPlace() {
+ try {
+ return mService.isInSignificantPlace();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 1eb466c..e3780ed 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -683,7 +683,9 @@
* <p>
* Read-only transactions may run concurrently with other read-only transactions, and if the
* database is in WAL mode, they may also run concurrently with IMMEDIATE or EXCLUSIVE
- * transactions.
+ * transactions. The {@code temp} schema may be modified during a read-only transaction;
+ * if the transaction is {@link #setTransactionSuccessful}, modifications to temp tables may
+ * be visible to some subsequent transactions.
* <p>
* Transactions can be nested. However, the behavior of the transaction is not altered by
* nested transactions. A nested transaction may be any of the three transaction types but if
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index ec67212..b2dcf90 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -739,6 +739,9 @@
* on is done.
*/
void onBlockingScreenOn(Runnable unblocker);
+
+ /** Whether auto brightness update in doze is allowed */
+ boolean allowAutoBrightnessInDoze();
}
/** A session token that associates a internal display with a {@link DisplayOffloader}. */
@@ -749,6 +752,9 @@
/** Whether the session is active. */
boolean isActive();
+ /** Whether auto brightness update in doze is allowed */
+ boolean allowAutoBrightnessInDoze();
+
/**
* Update the brightness from the offload chip.
* @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c611cb9..3e6223a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1948,6 +1948,10 @@
public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
+ // STATES bits that are used for Power Stats tracking
+ public static final int IMPORTANT_FOR_POWER_STATS_STATES =
+ STATE_GPS_ON_FLAG | STATE_SENSOR_ON_FLAG | STATE_AUDIO_ON_FLAG;
+
@UnsupportedAppUsage
public int states;
@@ -1988,6 +1992,10 @@
public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
+ // STATES2 bits that are used for Power Stats tracking
+ public static final int IMPORTANT_FOR_POWER_STATS_STATES2 =
+ STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG;
+
@UnsupportedAppUsage
public int states2;
@@ -2053,6 +2061,8 @@
public static final int EVENT_WAKEUP_AP = 0x0013;
// Event for reporting that a specific partial wake lock has been held for a long duration.
public static final int EVENT_LONG_WAKE_LOCK = 0x0014;
+ // Event for reporting change of some device states, triggered by a specific UID
+ public static final int EVENT_STATE_CHANGE = 0x0015;
// Number of event types.
public static final int EVENT_COUNT = 0x0016;
@@ -3066,13 +3076,13 @@
public static final String[] HISTORY_EVENT_NAMES = new String[] {
"null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
"active", "pkginst", "pkgunin", "alarm", "stats", "pkginactive", "pkgactive",
- "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity"
+ "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity", "state"
};
public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
"Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
"Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw",
- "Esw", "Ewa", "Elw", "Eec"
+ "Esw", "Ewa", "Elw", "Eec", "Esc"
};
@FunctionalInterface
@@ -3087,7 +3097,7 @@
sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sIntToString,
sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
- sUidToString, sUidToString, sUidToString, sIntToString
+ sUidToString, sUidToString, sUidToString, sIntToString, sUidToString
};
/**
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 17dfdda..71957ee 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -52,6 +52,8 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -388,7 +390,6 @@
* new file descriptor shared state such as file position with the
* original file descriptor.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
try {
final FileDescriptor fd = new FileDescriptor();
@@ -406,7 +407,6 @@
* new file descriptor shared state such as file position with the
* original file descriptor.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
public ParcelFileDescriptor dup() throws IOException {
if (mWrapped != null) {
return mWrapped.dup();
@@ -425,7 +425,6 @@
* @return Returns a new ParcelFileDescriptor holding a FileDescriptor
* for a dup of the given fd.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor fromFd(int fd) throws IOException {
final FileDescriptor original = new FileDescriptor();
setFdInt(original, fd);
@@ -485,7 +484,7 @@
*
* @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Socket.getFileDescriptor$()")
public static ParcelFileDescriptor fromSocket(Socket socket) {
FileDescriptor fd = socket.getFileDescriptor$();
try {
@@ -519,7 +518,7 @@
*
* @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "DatagramSocket.getFileDescriptor$()")
public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
FileDescriptor fd = datagramSocket.getFileDescriptor$();
try {
@@ -534,7 +533,6 @@
* ParcelFileDescriptor in the returned array is the read side; the second
* is the write side.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createPipe() throws IOException {
try {
final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
@@ -556,7 +554,6 @@
* calling {@link #checkError()}, usually after detecting an EOF.
* This can also be used to detect remote crashes.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
@@ -573,7 +570,7 @@
* Create two ParcelFileDescriptors structured as a pair of sockets
* connected to each other. The two sockets are indistinguishable.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.socketpair()")
public static ParcelFileDescriptor[] createSocketPair() throws IOException {
return createSocketPair(SOCK_STREAM);
}
@@ -581,7 +578,7 @@
/**
* @hide
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.socketpair()")
public static ParcelFileDescriptor[] createSocketPair(int type) throws IOException {
try {
final FileDescriptor fd0 = new FileDescriptor();
@@ -604,7 +601,7 @@
* calling {@link #checkError()}, usually after detecting an EOF.
* This can also be used to detect remote crashes.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.socketpair()")
public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException {
return createReliableSocketPair(SOCK_STREAM);
}
@@ -612,7 +609,7 @@
/**
* @hide
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.socketpair()")
public static ParcelFileDescriptor[] createReliableSocketPair(int type) throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
@@ -627,7 +624,7 @@
}
}
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.socketpair()")
private static FileDescriptor[] createCommSocketPair() throws IOException {
try {
// Use SOCK_SEQPACKET so that we have a guarantee that the status
@@ -656,7 +653,7 @@
*/
@UnsupportedAppUsage
@Deprecated
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(blockedBy = MemoryFile.class)
public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
if (data == null) return null;
MemoryFile file = new MemoryFile(name, data.length);
@@ -712,7 +709,7 @@
* @hide
*/
@TestApi
- @RavenwoodThrow(reason = "Requires kernel support")
+ @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
public static File getFile(FileDescriptor fd) throws IOException {
try {
final String path = Os.readlink("/proc/self/fd/" + getFdInt(fd));
@@ -744,7 +741,7 @@
* Return the total size of the file representing this fd, as determined by
* {@code stat()}. Returns -1 if the fd is not a file.
*/
- @RavenwoodThrow(reason = "Requires JNI support")
+ @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
public long getStatSize() {
if (mWrapped != null) {
return mWrapped.getStatSize();
@@ -769,7 +766,6 @@
* @hide
*/
@UnsupportedAppUsage
- @RavenwoodThrow(reason = "Requires JNI support")
public long seekTo(long pos) throws IOException {
if (mWrapped != null) {
return mWrapped.seekTo(pos);
@@ -1037,7 +1033,6 @@
* take care of calling {@link ParcelFileDescriptor#close
* ParcelFileDescriptor.close()} for you when the stream is closed.
*/
- @RavenwoodKeepWholeClass
public static class AutoCloseInputStream extends FileInputStream {
private final ParcelFileDescriptor mPfd;
@@ -1326,12 +1321,15 @@
}
- @RavenwoodThrow
+ @RavenwoodReplace
private static boolean isAtLeastQ() {
return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
}
- @RavenwoodThrow
+ private static boolean isAtLeastQ$ravenwood() {
+ return RavenwoodEnvironment.workaround().isTargetSdkAtLeastQ();
+ }
+
private static int ifAtLeastQ(int value) {
return isAtLeastQ() ? value : 0;
}
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index ee5e533..fe7eab7 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -81,6 +81,13 @@
}
flag {
+ name: "significant_places"
+ namespace: "biometrics"
+ description: "Enabled significant place monitoring"
+ bug: "337870680"
+}
+
+flag {
name: "report_primary_auth_attempts"
namespace: "biometrics"
description: "Report primary auth attempts from LockSettingsService"
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1fc98cf..a9846fb 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1262,15 +1262,11 @@
mHost.getInputMethodManager(), null /* icProto */);
}
- final var statsToken = (types & ime()) == 0 ? null
- : ImeTracker.forLogging().onStart(ImeTracker.TYPE_USER,
- ImeTracker.ORIGIN_CLIENT,
- SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
- mHost.isHandlingPointerEvent() /* fromUser */);
+ // TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType,
getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
- false /* useInsetsAnimationThread */, statsToken);
+ false /* useInsetsAnimationThread */, null);
}
private void controlAnimationUnchecked(@InsetsType int types,
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 97f8084..9029685 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -68,6 +68,7 @@
mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT,
android.content.IntentSender.class);
String targetPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ Log.i(TAG, "Unlaunchable activity for target package: " + targetPackageName);
final UserManager userManager = UserManager.get(this);
if (mUserId == UserHandle.USER_NULL) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index e6af64a..244165f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -16,6 +16,10 @@
package com.android.internal.os;
+import static android.os.BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+import static android.os.BatteryStats.HistoryItem.EVENT_FLAG_START;
+import static android.os.BatteryStats.HistoryItem.EVENT_STATE_CHANGE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryConsumer;
@@ -1449,6 +1453,21 @@
}
/**
+ * Records an event when some state flag changes to true.
+ */
+ public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+ int uid, String name) {
+ synchronized (this) {
+ mHistoryCur.states |= stateFlags;
+ mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_START;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.uid = uid;
+ mHistoryCur.eventTag.string = name;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
+ /**
* Records an event when some state flag changes to false.
*/
public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
@@ -1459,6 +1478,21 @@
}
/**
+ * Records an event when some state flag changes to false.
+ */
+ public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+ int uid, String name) {
+ synchronized (this) {
+ mHistoryCur.states &= ~stateFlags;
+ mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_FINISH;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.uid = uid;
+ mHistoryCur.eventTag.string = name;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
+ /**
* Records an event when some state flags change to true and some to false.
*/
public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags,
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 1494425..8fe1813 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -27,6 +27,7 @@
public static final String TAG = "RavenwoodEnvironment";
private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
+ private static Workaround sWorkaround = new Workaround();
private RavenwoodEnvironment() {
if (isRunningOnRavenwood()) {
@@ -76,4 +77,30 @@
private boolean isRunningOnRavenwood$ravenwood() {
return true;
}
+
+ /**
+ * See {@link Workaround}. It's only usablke on Ravenwood.
+ */
+ public static Workaround workaround() {
+ if (getInstance().isRunningOnRavenwood()) {
+ return sWorkaround;
+ }
+ throw new IllegalStateException("Workaround can only be used on Ravenwood");
+ }
+
+ /**
+ * A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
+ * be empty, and all its APIs should be able to be implemented properly.
+ */
+ public static class Workaround {
+ Workaround() {
+ }
+
+ /**
+ * @return whether the app's target SDK level is at least Q.
+ */
+ public boolean isTargetSdkAtLeastQ() {
+ return true;
+ }
+ }
}
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 6f1c763..8f70268 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -436,7 +436,7 @@
int result = SQLITE_OK;
if (connection->tableQuery == nullptr) {
static char const* sql =
- "SELECT COUNT(*) FROM tables_used(?) WHERE schema != 'temp' AND wr != 0";
+ "SELECT NULL FROM tables_used(?) WHERE schema != 'temp' AND wr != 0";
result = sqlite3_prepare_v2(connection->db, sql, -1, &connection->tableQuery, nullptr);
if (result != SQLITE_OK) {
ALOGE("failed to compile query table: %s",
@@ -447,25 +447,20 @@
// A temporary, to simplify the code.
sqlite3_stmt* query = connection->tableQuery;
- sqlite3_reset(query);
- sqlite3_clear_bindings(query);
result = sqlite3_bind_text(query, 1, sqlite3_sql(statement), -1, SQLITE_STATIC);
if (result != SQLITE_OK) {
ALOGE("tables bind pointer returns %s", sqlite3_errstr(result));
- return false;
}
result = sqlite3_step(query);
- if (result != SQLITE_ROW) {
- ALOGE("tables query error: %d/%s", result, sqlite3_errstr(result));
- // Make sure the query is no longer bound to the statement.
- sqlite3_clear_bindings(query);
- return false;
- }
-
- int count = sqlite3_column_int(query, 0);
- // Make sure the query is no longer bound to the statement SQL string.
+ // Make sure the query is no longer bound to the statement SQL string and
+ // that is no longer holding any table locks.
+ sqlite3_reset(query);
sqlite3_clear_bindings(query);
- return count == 0;
+
+ if (result != SQLITE_ROW && result != SQLITE_DONE) {
+ ALOGE("tables query error: %d/%s", result, sqlite3_errstr(result));
+ }
+ return result == SQLITE_DONE;
}
static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2068bd7..3006e20 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1411,8 +1411,10 @@
return JNI_TRUE;
}
- env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback, getpid(),
- code, flags, err);
+ if (err == FAILED_TRANSACTION) {
+ env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback,
+ getpid(), code, flags, err);
+ }
if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 9ad577a..85d34e2 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -133,6 +133,8 @@
<item name="colorControlActivated">?attr/colorControlHighlight</item>
<item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
<item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
+ <item name="iconfactoryIconSize">@dimen/resolver_icon_size</item>
+ <item name="iconfactoryBadgeSize">@dimen/resolver_badge_size</item>
</style>
<!-- Use a dark theme for watches. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0676f72..1112e65 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1193,13 +1193,14 @@
<!-- Allows activities to be launched on a long press on power during device setup. -->
<bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool>
- <!-- Control the behavior when the user short presses the settings button.
- 0 - Nothing
+ <!-- Control the behavior when the user presses the settings button.
+ 0 - Launch Settings activity
1 - Launch notification panel
+ 2 - Nothing
This needs to match the constants in
com/android/server/policy/PhoneWindowManager.java
-->
- <integer name="config_shortPressOnSettingsBehavior">0</integer>
+ <integer name="config_settingsKeyBehavior">0</integer>
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 0b351b4..04f6f52 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -309,7 +309,7 @@
fresh, it will be used as the current location by Telephony to decide whether satellite
services should be allowed.
-->
- <integer name="config_oem_enabled_satellite_location_fresh_duration">600</integer>
+ <integer name="config_oem_enabled_satellite_location_fresh_duration">300</integer>
<java-symbol type="integer" name="config_oem_enabled_satellite_location_fresh_duration" />
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b885e03..a0807ca 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -72,6 +72,9 @@
<!-- The default margin used in immersive mode to capture the start of a swipe gesture from the
edge of the screen to show the system bars. -->
<dimen name="system_gestures_start_threshold">24dp</dimen>
+ <!-- The minimum swipe gesture distance for showing the system bars when in immersive mode. This
+ swipe must be within the specified system_gestures_start_threshold area. -->
+ <dimen name="system_gestures_distance_threshold">24dp</dimen>
<!-- Height of the bottom navigation bar frame; this is different than navigation_bar_height
where that is the height reported to all the other windows to resize themselves around the
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e2c111..0d2fd1c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1804,6 +1804,7 @@
<java-symbol type="dimen" name="taskbar_frame_height" />
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
+ <java-symbol type="dimen" name="system_gestures_distance_threshold" />
<java-symbol type="dimen" name="system_gestures_start_threshold" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
@@ -1860,7 +1861,7 @@
<java-symbol type="integer" name="config_lidNavigationAccessibility" />
<java-symbol type="integer" name="config_lidOpenRotation" />
<java-symbol type="integer" name="config_longPressOnHomeBehavior" />
- <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" />
+ <java-symbol type="integer" name="config_settingsKeyBehavior" />
<java-symbol type="layout" name="global_actions" />
<java-symbol type="layout" name="global_actions_item" />
<java-symbol type="layout" name="global_actions_silent_mode" />
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index e118c98d..c4695d9 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -375,6 +375,8 @@
assertEquals(3, s.getColumnInt(0));
}
+ mDatabase.execSQL("DROP TABLE t1");
+
} catch (SQLiteException e) {
allowed = false;
} finally {
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
index 78c8881..297c490 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="8"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="18"/>
<application android:name="com.android.multidexlegacyandexception.TestApplication"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml
index 1a60c1e..a208268 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="8"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="18"/>
<application android:name=".TestApplication"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/AndroidManifest.xml
index 35369c7..bb2a201 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="8" />
+ <uses-sdk android:minSdkVersion="21" />
<instrumentation
android:name="com.android.test.runner.MultiDexTestRunner"
android:targetPackage="com.android.multidexlegacytestapp" />
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/AndroidManifest.xml
index 1cadfcd..b96566c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="8" />
+ <uses-sdk android:minSdkVersion="21" />
<instrumentation
android:name="com.android.multidexlegacytestapp.test2.MultiDexAndroidJUnitRunner"
android:targetPackage="com.android.multidexlegacytestapp" />
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
index 840daab..3ad61ca 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="19"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="19"/>
<application android:name="androidx.multidex.MultiDexApplication"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/AndroidManifest.xml
index e2fba4e..c644c36 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="9" />
+ <uses-sdk android:minSdkVersion="21" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.framework.multidexlegacytestservices" />
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
index 01285e7..f511c5f 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/AndroidManifest.xml
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="9" />
+ <uses-sdk android:minSdkVersion="21" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
index 8c911c4..4730243 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="9"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="18"/>
<application android:name="androidx.multidex.MultiDexApplication"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
index 1817e95..0bcf9fe 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="2"
android:versionName="2.0">
- <uses-sdk android:minSdkVersion="9"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="18"/>
<application android:name="androidx.multidex.MultiDexApplication"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
index c8a41bc..5b7680d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionCode="3"
android:versionName="3.0">
- <uses-sdk android:minSdkVersion="9"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="18"/>
<application android:name="androidx.multidex.MultiDexApplication"
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 7823277..f9fb84d 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -134,4 +134,19 @@
<install-in-user-type package="com.android.avatarpicker">
<install-in user-type="FULL" />
</install-in-user-type>
+
+ <!-- AiLabs Warp app pre-installed in hardware/google/pixel/common/pixel-common-device.mk -->
+ <install-in-user-type package="com.google.android.apps.warp">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
+ </install-in-user-type>
+
+ <!-- Google Home app pre-installed on tangor devices in vendor/google/products/tangor_common.mk
+ -->
+ <install-in-user-type package="com.google.android.apps.chromecast.app">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
+ </install-in-user-type>
</config>
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 67f1650..08e695b 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -92,3 +92,10 @@
description: "Enables Taskbar on phones"
bug: "341784466"
}
+
+flag {
+ name: "enable_bubble_anything"
+ namespace: "multitasking"
+ description: "Enable UI affordances to put other content into a bubble"
+ bug: "342245211"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 35a4a62..0efdbdc 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -23,6 +23,7 @@
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.UserHandle
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
@@ -33,6 +34,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.common.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
@@ -44,19 +46,24 @@
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
-import java.util.concurrent.Semaphore
-import java.util.concurrent.TimeUnit
-import java.util.function.Consumer
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
/** Unit tests for [BubbleStackView]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class BubbleStackViewTest {
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
private lateinit var iconFactory: BubbleIconFactory
@@ -66,6 +73,8 @@
private lateinit var windowManager: IWindowManager
private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
private lateinit var bubbleData: BubbleData
+ private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
+ private var sysuiProxy = mock<SysuiProxy>()
@Before
fun setUp() {
@@ -86,7 +95,6 @@
)
)
positioner = BubblePositioner(context, windowManager)
- val bubbleStackViewManager = FakeBubbleStackViewManager()
bubbleData =
BubbleData(
context,
@@ -95,8 +103,7 @@
BubbleEducationController(context),
shellExecutor
)
-
- val sysuiProxy = mock<SysuiProxy>()
+ bubbleStackViewManager = FakeBubbleStackViewManager()
expandedViewManager = FakeBubbleExpandedViewManager()
bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
bubbleStackView =
@@ -234,6 +241,115 @@
.inOrder()
}
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun testCreateStackView_noOverflowContents_noOverflow() {
+ bubbleStackView =
+ BubbleStackView(
+ context,
+ bubbleStackViewManager,
+ positioner,
+ bubbleData,
+ null,
+ FloatingContentCoordinator(),
+ { sysuiProxy },
+ shellExecutor
+ )
+
+ assertThat(bubbleData.overflowBubbles).isEmpty()
+ val bubbleOverflow = bubbleData.overflow
+ // Overflow shouldn't be attached
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun testCreateStackView_hasOverflowContents_hasOverflow() {
+ // Add a bubble to the overflow
+ val bubble1 = createAndInflateChatBubble(key = "bubble1")
+ bubbleData.notificationEntryUpdated(bubble1, false, false)
+ bubbleData.dismissBubbleWithKey(bubble1.key, Bubbles.DISMISS_USER_GESTURE)
+ assertThat(bubbleData.overflowBubbles).isNotEmpty()
+
+ bubbleStackView =
+ BubbleStackView(
+ context,
+ bubbleStackViewManager,
+ positioner,
+ bubbleData,
+ null,
+ FloatingContentCoordinator(),
+ { sysuiProxy },
+ shellExecutor
+ )
+ val bubbleOverflow = bubbleData.overflow
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun testCreateStackView_noOverflowContents_hasOverflow() {
+ bubbleStackView =
+ BubbleStackView(
+ context,
+ bubbleStackViewManager,
+ positioner,
+ bubbleData,
+ null,
+ FloatingContentCoordinator(),
+ { sysuiProxy },
+ shellExecutor
+ )
+
+ assertThat(bubbleData.overflowBubbles).isEmpty()
+ val bubbleOverflow = bubbleData.overflow
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun showOverflow_true() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.showOverflow(true)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ val bubbleOverflow = bubbleData.overflow
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun showOverflow_false() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.showOverflow(true)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ val bubbleOverflow = bubbleData.overflow
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.showOverflow(false)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ // The overflow should've been removed
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isEqualTo(-1)
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ fun showOverflow_ignored() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.showOverflow(false)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ // showOverflow should've been ignored, so the overflow would be attached
+ val bubbleOverflow = bubbleData.overflow
+ assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
+ }
+
private fun createAndInflateChatBubble(key: String): Bubble {
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
diff --git a/libs/WindowManager/Shell/res/drawable/circular_progress.xml b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
index 294b1f0..0d64527 100644
--- a/libs/WindowManager/Shell/res/drawable/circular_progress.xml
+++ b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
@@ -24,7 +24,7 @@
android:toDegrees="275">
<shape
android:shape="ring"
- android:thickness="3dp"
+ android:thickness="2dp"
android:innerRadius="14dp"
android:useLevel="true">
</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
index 5d9fe67..9566f2f 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<shape android:shape="rectangle"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@android:color/white" />
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow" />
<corners android:radius="@dimen/desktop_mode_maximize_menu_corner_radius" />
</shape>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 4d06dd3..84e1449 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -28,13 +28,11 @@
<LinearLayout
android:id="@+id/open_menu_button"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:tint="?androidprv:attr/materialColorOnSurface"
- android:background="?android:selectableItemBackground"
+ android:layout_height="40dp"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
- android:paddingStart="12dp">
+ android:layout_marginStart="12dp">
<ImageView
android:id="@+id/application_icon"
android:layout_width="@dimen/desktop_mode_caption_icon_radius"
@@ -85,8 +83,6 @@
android:layout_height="40dp"
android:layout_gravity="end"
android:layout_marginHorizontal="8dp"
- android:paddingHorizontal="5dp"
- android:paddingVertical="3dp"
android:clickable="true"
android:focusable="true"/>
@@ -97,7 +93,6 @@
android:paddingHorizontal="10dp"
android:paddingVertical="8dp"
android:layout_marginEnd="8dp"
- android:background="?android:selectableItemBackgroundBorderless"
android:contentDescription="@string/close_button_text"
android:src="@drawable/desktop_mode_header_ic_close"
android:scaleType="centerCrop"
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index 77507a4..cf1b894 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -16,20 +16,28 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <ProgressBar
- android:id="@+id/progress_bar"
- style="?android:attr/progressBarStyleHorizontal"
- android:progressDrawable="@drawable/circular_progress"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:indeterminate="false"
- android:visibility="invisible"/>
+
+ <FrameLayout
+ android:layout_width="44dp"
+ android:layout_height="40dp">
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:progressDrawable="@drawable/circular_progress"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:indeterminate="false"
+ android:layout_marginHorizontal="6dp"
+ android:layout_marginVertical="4dp"
+ android:visibility="invisible"/>
+ </FrameLayout>
<ImageButton
android:id="@+id/maximize_window"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:padding="5dp"
+ android:layout_width="44dp"
+ android:layout_height="40dp"
+ android:paddingHorizontal="10dp"
+ android:paddingVertical="8dp"
android:contentDescription="@string/maximize_button_text"
android:src="@drawable/decor_desktop_mode_maximize_button_dark"
android:scaleType="fitCenter" />
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8bc3004..f27f46c 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -553,6 +553,25 @@
<!-- The corner radius of a task that was dragged from fullscreen. -->
<dimen name="desktop_mode_dragged_task_radius">28dp</dimen>
+ <!-- The corner radius of the app chip, maximize and close button's ripple drawable -->
+ <dimen name="desktop_mode_header_buttons_ripple_radius">16dp</dimen>
+ <!-- The vertical inset to apply to the app chip's ripple drawable -->
+ <dimen name="desktop_mode_header_app_chip_ripple_inset_vertical">4dp</dimen>
+
+ <!-- The corner radius of the maximize button's ripple drawable -->
+ <dimen name="desktop_mode_header_maximize_ripple_radius">18dp</dimen>
+ <!-- The vertical inset to apply to the maximize button's ripple drawable -->
+ <dimen name="desktop_mode_header_maximize_ripple_inset_vertical">4dp</dimen>
+ <!-- The horizontal inset to apply to the maximize button's ripple drawable -->
+ <dimen name="desktop_mode_header_maximize_ripple_inset_horizontal">6dp</dimen>
+
+ <!-- The corner radius of the close button's ripple drawable -->
+ <dimen name="desktop_mode_header_close_ripple_radius">18dp</dimen>
+ <!-- The vertical inset to apply to the close button's ripple drawable -->
+ <dimen name="desktop_mode_header_close_ripple_inset_vertical">4dp</dimen>
+ <!-- The horizontal inset to apply to the close button's ripple drawable -->
+ <dimen name="desktop_mode_header_close_ripple_inset_horizontal">6dp</dimen>
+
<!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
<item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
<!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
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 05d8637..ee740fb 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
@@ -131,6 +131,11 @@
abstract fun preparePreCommitEnteringRectMovement()
/**
+ * Subclasses must provide a duration (in ms) for the post-commit part of the animation
+ */
+ abstract fun getPostCommitAnimationDuration(): Long
+
+ /**
* Returns a base transformation to apply to the entering target during pre-commit. The system
* will apply the default animation on top of it.
*/
@@ -259,7 +264,8 @@
.setSpring(postCommitFlingSpring)
flingAnimation.start()
- val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_COMMIT_DURATION)
+ val valueAnimator =
+ ValueAnimator.ofFloat(1f, 0f).setDuration(getPostCommitAnimationDuration())
valueAnimator.addUpdateListener { animation: ValueAnimator ->
val progress = animation.animatedFraction
onPostCommitProgress(progress)
@@ -522,7 +528,6 @@
internal const val MAX_SCALE = 0.9f
private const val MAX_SCRIM_ALPHA_DARK = 0.8f
private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
- private const val POST_COMMIT_DURATION = 300L
private const val SPRING_SCALE = 100f
private const val MAX_FLING_SCALE = 0.6f
}
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 ab359bd..c4aafea 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
@@ -33,6 +33,8 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.shared.annotations.ShellMainThread
import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
/** Class that handles customized predictive cross activity back animations. */
@ShellMainThread
@@ -96,6 +98,12 @@
targetEnteringRect.set(startClosingRect)
}
+ override fun getPostCommitAnimationDuration(): Long {
+ return min(
+ MAX_POST_COMMIT_ANIM_DURATION, max(closeAnimation!!.duration, enterAnimation!!.duration)
+ )
+ }
+
override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
gestureProgress = progress
transformation.clear()
@@ -107,9 +115,9 @@
super.startBackAnimation(backMotionEvent)
if (
closeAnimation == null ||
- enterAnimation == null ||
- closingTarget == null ||
- enteringTarget == null
+ enterAnimation == null ||
+ closingTarget == null ||
+ enteringTarget == null
) {
ProtoLog.d(
ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
@@ -125,10 +133,13 @@
super.onPostCommitProgress(linearProgress)
if (closingTarget == null || enteringTarget == null) return
- // TODO: Should we use the duration from the custom xml spec for the post-commit animation?
- applyTransform(closingTarget!!.leash, currentClosingRect, linearProgress, closeAnimation!!)
- val enteringProgress =
- MathUtils.lerp(gestureProgress * PRE_COMMIT_MAX_PROGRESS, 1f, linearProgress)
+ val closingProgress = closeAnimation!!.getPostCommitProgress(linearProgress)
+ applyTransform(closingTarget!!.leash, currentClosingRect, closingProgress, closeAnimation!!)
+ val enteringProgress = MathUtils.lerp(
+ gestureProgress * PRE_COMMIT_MAX_PROGRESS,
+ 1f,
+ enterAnimation!!.getPostCommitProgress(linearProgress)
+ )
applyTransform(
enteringTarget!!.leash,
currentEnteringRect,
@@ -175,6 +186,19 @@
return false
}
+ private fun Animation.getPostCommitProgress(linearProgress: Float): Float {
+ return when (duration) {
+ 0L -> 1f
+ else -> min(
+ 1f,
+ getPostCommitAnimationDuration() / min(
+ MAX_POST_COMMIT_ANIM_DURATION,
+ duration
+ ).toFloat() * linearProgress
+ )
+ }
+ }
+
class AnimationLoadResult {
var closeAnimation: Animation? = null
var enterAnimation: Animation? = null
@@ -183,6 +207,7 @@
companion object {
private const val PRE_COMMIT_MAX_PROGRESS = 0.2f
+ private const val MAX_POST_COMMIT_ANIM_DURATION = 2000L
}
}
@@ -226,7 +251,7 @@
// Try to get animation from Activity#overrideActivityTransition
if (
enterAnimation && animationInfo.customEnterAnim != 0 ||
- !enterAnimation && animationInfo.customExitAnim != 0
+ !enterAnimation && animationInfo.customExitAnim != 0
) {
a =
transitionAnimation.loadAppTransitionAnimation(
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 9f07e5b..44752fe 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
@@ -71,6 +71,8 @@
targetEnteringRect.scaleCentered(MAX_SCALE)
}
+ override fun getPostCommitAnimationDuration() = POST_COMMIT_DURATION
+
override fun onGestureCommitted(velocity: Float) {
// We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
// coordinate of the gesture driven phase. Let's update the start and target rects and kick
@@ -93,4 +95,9 @@
applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
applyTransaction()
}
+
+
+ companion object {
+ private const val POST_COMMIT_DURATION = 300L
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 32e7b57..317e00a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1772,7 +1772,7 @@
if (groupKey == null) {
return bubbleChildren;
}
- for (Bubble bubble : mBubbleData.getActiveBubbles()) {
+ for (Bubble bubble : mBubbleData.getBubbles()) {
if (bubble.getGroupKey() != null && groupKey.equals(bubble.getGroupKey())) {
bubbleChildren.add(bubble);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 26483c8..874102c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -339,11 +339,6 @@
return mOverflow;
}
- /** Return a read-only current active bubble lists. */
- public List<Bubble> getActiveBubbles() {
- return Collections.unmodifiableList(mBubbles);
- }
-
public void setExpanded(boolean expanded) {
setExpandedInternal(expanded);
dispatchPendingChanges();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 69bf5fd..bc63db3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -3541,7 +3541,7 @@
*/
int getBubbleIndex(@Nullable BubbleViewProvider provider) {
if (provider == null) {
- return 0;
+ return -1;
}
return mBubbleContainer.indexOfChild(provider.getIconView());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 95d4714..109868d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -20,9 +20,7 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.util.KtProtoLog
-/**
- * Event logger for logging desktop mode session events
- */
+/** Event logger for logging desktop mode session events */
class DesktopModeEventLogger {
/**
* Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for
@@ -32,13 +30,16 @@
KtProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session enter, session: %s reason: %s",
- sessionId, enterReason.name
+ sessionId,
+ enterReason.name
)
- FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID,
+ FrameworkStatsLog.write(
+ DESKTOP_MODE_ATOM_ID,
/* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER,
/* enterReason */ enterReason.reason,
/* exitReason */ 0,
- /* session_id */ sessionId)
+ /* session_id */ sessionId
+ )
}
/**
@@ -49,13 +50,16 @@
KtProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session exit, session: %s reason: %s",
- sessionId, exitReason.name
+ sessionId,
+ exitReason.name
)
- FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID,
+ FrameworkStatsLog.write(
+ DESKTOP_MODE_ATOM_ID,
/* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT,
/* enterReason */ 0,
/* exitReason */ exitReason.reason,
- /* session_id */ sessionId)
+ /* session_id */ sessionId
+ )
}
/**
@@ -66,9 +70,11 @@
KtProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task added, session: %s taskId: %s",
- sessionId, taskUpdate.instanceId
+ sessionId,
+ taskUpdate.instanceId
)
- FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ FrameworkStatsLog.write(
+ DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
/* task_event */
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
/* instance_id */
@@ -84,7 +90,8 @@
/* task_y */
taskUpdate.taskY,
/* session_id */
- sessionId)
+ sessionId
+ )
}
/**
@@ -95,9 +102,11 @@
KtProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task remove, session: %s taskId: %s",
- sessionId, taskUpdate.instanceId
+ sessionId,
+ taskUpdate.instanceId
)
- FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ FrameworkStatsLog.write(
+ DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
/* task_event */
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
/* instance_id */
@@ -113,7 +122,8 @@
/* task_y */
taskUpdate.taskY,
/* session_id */
- sessionId)
+ sessionId
+ )
}
/**
@@ -124,9 +134,11 @@
KtProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
- sessionId, taskUpdate.instanceId
+ sessionId,
+ taskUpdate.instanceId
)
- FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
+ FrameworkStatsLog.write(
+ DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
/* task_event */
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
/* instance_id */
@@ -142,7 +154,8 @@
/* task_y */
taskUpdate.taskY,
/* session_id */
- sessionId)
+ sessionId
+ )
}
companion object {
@@ -160,12 +173,8 @@
* stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
*/
enum class EnterReason(val reason: Int) {
- UNKNOWN_ENTER(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER
- ),
- OVERVIEW(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW
- ),
+ UNKNOWN_ENTER(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER),
+ OVERVIEW(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW),
APP_HANDLE_DRAG(
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_DRAG
),
@@ -178,9 +187,7 @@
KEYBOARD_SHORTCUT_ENTER(
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER
),
- SCREEN_ON(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON
- );
+ SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON)
}
/**
@@ -188,12 +195,8 @@
* stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
*/
enum class ExitReason(val reason: Int) {
- UNKNOWN_EXIT(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT
- ),
- DRAG_TO_EXIT(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT
- ),
+ UNKNOWN_EXIT(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT),
+ DRAG_TO_EXIT(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
APP_HANDLE_MENU_BUTTON_EXIT(
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__APP_HANDLE_MENU_BUTTON_EXIT
),
@@ -203,16 +206,12 @@
RETURN_HOME_OR_OVERVIEW(
FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME
),
- TASK_FINISHED(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED
- ),
- SCREEN_OFF(
- FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF
- )
+ TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
+ SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
}
private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED
private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID =
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 0b7a3e8..5d8e340 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -60,8 +60,9 @@
private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) }
init {
- if (Transitions.ENABLE_SHELL_TRANSITIONS &&
- DesktopModeStatus.canEnterDesktopMode(context)) {
+ if (
+ Transitions.ENABLE_SHELL_TRANSITIONS && DesktopModeStatus.canEnterDesktopMode(context)
+ ) {
shellInit.addInitCallback(this::onInit, this)
}
}
@@ -350,4 +351,4 @@
return this.type == WindowManager.TRANSIT_TO_FRONT &&
this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index f1a475a..bc27f34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -20,9 +20,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler
import java.io.PrintWriter
-/**
- * Handles the shell commands for the DesktopTasksController.
- */
+/** Handles the shell commands for the DesktopTasksController. */
class DesktopModeShellCommandHandler(private val controller: DesktopTasksController) :
ShellCommandHandler.ShellCommandActionHandler {
@@ -58,12 +56,13 @@
return false
}
- val taskId = try {
- args[1].toInt()
- } catch (e: NumberFormatException) {
- pw.println("Error: task id should be an integer")
- return false
- }
+ val taskId =
+ try {
+ args[1].toInt()
+ } catch (e: NumberFormatException) {
+ pw.println("Error: task id should be an integer")
+ return false
+ }
return controller.moveToDesktop(taskId, WindowContainerTransaction())
}
@@ -75,12 +74,13 @@
return false
}
- val taskId = try {
- args[1].toInt()
- } catch (e: NumberFormatException) {
- pw.println("Error: task id should be an integer")
- return false
- }
+ val taskId =
+ try {
+ args[1].toInt()
+ } catch (e: NumberFormatException) {
+ pw.println("Error: task id should be an integer")
+ return false
+ }
controller.moveToNextDisplay(taskId)
return true
@@ -92,4 +92,4 @@
pw.println("$prefix moveToNextDisplay <taskId> ")
pw.println("$prefix Move a task with given id to next display.")
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 7e0234e..7d01580 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -32,9 +32,7 @@
import java.util.concurrent.Executor
import java.util.function.Consumer
-/**
- * Keeps track of task data related to desktop mode.
- */
+/** Keeps track of task data related to desktop mode. */
class DesktopModeTaskRepository {
/** Task data that is tracked per display */
@@ -48,12 +46,12 @@
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
val minimizedTasks: ArraySet<Int> = ArraySet(),
+ // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
+ val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
)
// Token of the current wallpaper activity, used to remove it when the last task is removed
var wallpaperActivityToken: WindowContainerToken? = null
- // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
- private val freeformTasksInZOrder = mutableListOf<Int>()
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
// Track visible tasks separately because a task may be part of the desktop but not visible.
private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
@@ -84,13 +82,8 @@
activeTasksListeners.add(activeTasksListener)
}
- /**
- * Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not.
- */
- fun addVisibleTasksListener(
- visibleTasksListener: VisibleTasksListener,
- executor: Executor
- ) {
+ /** Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not. */
+ fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
visibleTasksListeners[visibleTasksListener] = executor
displayData.keyIterator().forEach { displayId ->
val visibleTasksCount = getVisibleTaskCount(displayId)
@@ -112,9 +105,7 @@
}
}
- /**
- * Create a new merged region representative of all exclusion regions in all desktop tasks.
- */
+ /** Create a new merged region representative of all exclusion regions in all desktop tasks. */
private fun calculateDesktopExclusionRegion(): Region {
val desktopExclusionRegion = Region()
desktopExclusionRegions.valueIterator().forEach { taskExclusionRegion ->
@@ -123,16 +114,12 @@
return desktopExclusionRegion
}
- /**
- * Remove a previously registered [ActiveTasksListener]
- */
+ /** Remove a previously registered [ActiveTasksListener] */
fun removeActiveTasksListener(activeTasksListener: ActiveTasksListener) {
activeTasksListeners.remove(activeTasksListener)
}
- /**
- * Remove a previously registered [VisibleTasksListener]
- */
+ /** Remove a previously registered [VisibleTasksListener] */
fun removeVisibleTasksListener(visibleTasksListener: VisibleTasksListener) {
visibleTasksListeners.remove(visibleTasksListener)
}
@@ -182,18 +169,14 @@
return result
}
- /**
- * Check if a task with the given [taskId] was marked as an active task
- */
+ /** Check if a task with the given [taskId] was marked as an active task */
fun isActiveTask(taskId: Int): Boolean {
return displayData.valueIterator().asSequence().any { data ->
data.activeTasks.contains(taskId)
}
}
- /**
- * Whether a task is visible.
- */
+ /** Whether a task is visible. */
fun isVisibleTask(taskId: Int): Boolean {
return displayData.valueIterator().asSequence().any { data ->
data.visibleTasks.contains(taskId)
@@ -207,18 +190,14 @@
}
}
- /**
- * Check if a task with the given [taskId] is the only active task on its display
- */
+ /** Check if a task with the given [taskId] is the only active task on its display */
fun isOnlyActiveTask(taskId: Int): Boolean {
return displayData.valueIterator().asSequence().any { data ->
data.activeTasks.singleOrNull() == taskId
}
}
- /**
- * Get a set of the active tasks for given [displayId]
- */
+ /** Get a set of the active tasks for given [displayId] */
fun getActiveTasks(displayId: Int): ArraySet<Int> {
return ArraySet(displayData[displayId]?.activeTasks)
}
@@ -235,20 +214,16 @@
*/
fun getActiveNonMinimizedTasksOrderedFrontToBack(displayId: Int): List<Int> {
val activeTasks = getActiveTasks(displayId)
- val allTasksInZOrder = getFreeformTasksInZOrder()
+ val allTasksInZOrder = getFreeformTasksInZOrder(displayId)
return activeTasks
- // Don't show already minimized Tasks
- .filter { taskId -> !isMinimizedTask(taskId) }
- .sortedBy { taskId -> allTasksInZOrder.indexOf(taskId) }
+ // Don't show already minimized Tasks
+ .filter { taskId -> !isMinimizedTask(taskId) }
+ .sortedBy { taskId -> allTasksInZOrder.indexOf(taskId) }
}
- /**
- * Get a list of freeform tasks, ordered from top-bottom (top at index 0).
- */
- // TODO(b/278084491): pass in display id
- fun getFreeformTasksInZOrder(): List<Int> {
- return freeformTasksInZOrder
- }
+ /** Get a list of freeform tasks, ordered from top-bottom (top at index 0). */
+ fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
+ ArrayList(displayData[displayId]?.freeformTasksInZOrder ?: emptyList())
/**
* Updates whether a freeform task with this id is visible or not and notifies listeners.
@@ -262,8 +237,10 @@
val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId }
for (otherDisplayId in otherDisplays) {
if (displayData[otherDisplayId].visibleTasks.remove(taskId)) {
- notifyVisibleTaskListeners(otherDisplayId,
- displayData[otherDisplayId].visibleTasks.size)
+ notifyVisibleTaskListeners(
+ otherDisplayId,
+ displayData[otherDisplayId].visibleTasks.size
+ )
}
}
} else if (displayId == INVALID_DISPLAY) {
@@ -310,9 +287,7 @@
}
}
- /**
- * Get number of tasks that are marked as visible on given [displayId]
- */
+ /** Get number of tasks that are marked as visible on given [displayId] */
fun getVisibleTaskCount(displayId: Int): Int {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
@@ -322,60 +297,62 @@
return displayData[displayId]?.visibleTasks?.size ?: 0
}
- /**
- * Add (or move if it already exists) the task to the top of the ordered list.
- */
- fun addOrMoveFreeformTaskToTop(taskId: Int) {
+ /** Add (or move if it already exists) the task to the top of the ordered list. */
+ // TODO(b/342417921): Identify if there is additional checks needed to move tasks for
+ // multi-display scenarios.
+ fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTaskRepo: add or move task to top taskId=%d",
+ "DesktopTaskRepo: add or move task to top: display=%d, taskId=%d",
+ displayId,
taskId
)
- if (freeformTasksInZOrder.contains(taskId)) {
- freeformTasksInZOrder.remove(taskId)
- }
- freeformTasksInZOrder.add(0, taskId)
+ displayData[displayId]?.freeformTasksInZOrder?.remove(taskId)
+ displayData.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
}
/** Mark a Task as minimized. */
fun minimizeTask(displayId: Int, taskId: Int) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopModeTaskRepository: minimize Task: display=%d, task=%d",
- displayId, taskId)
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeTaskRepository: minimize Task: display=%d, task=%d",
+ displayId,
+ taskId
+ )
displayData.getOrCreate(displayId).minimizedTasks.add(taskId)
}
/** Mark a Task as non-minimized. */
fun unminimizeTask(displayId: Int, taskId: Int) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopModeTaskRepository: unminimize Task: display=%d, task=%d",
- displayId, taskId)
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeTaskRepository: unminimize Task: display=%d, task=%d",
+ displayId,
+ taskId
+ )
displayData[displayId]?.minimizedTasks?.remove(taskId)
}
- /**
- * Remove the task from the ordered list.
- */
- fun removeFreeformTask(taskId: Int) {
+ /** Remove the task from the ordered list. */
+ fun removeFreeformTask(displayId: Int, taskId: Int) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTaskRepo: remove freeform task from ordered list taskId=%d",
+ "DesktopTaskRepo: remove freeform task from ordered list: display=%d, taskId=%d",
+ displayId,
taskId
)
- freeformTasksInZOrder.remove(taskId)
+ displayData[displayId]?.freeformTasksInZOrder?.remove(taskId)
boundsBeforeMaximizeByTaskId.remove(taskId)
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTaskRepo: remaining freeform tasks: %s", freeformTasksInZOrder.toDumpString(),
+ "DesktopTaskRepo: remaining freeform tasks: %s",
+ displayData[displayId]?.freeformTasksInZOrder?.toDumpString() ?: ""
)
}
/**
* Updates the active desktop gesture exclusion regions; if desktopExclusionRegions has been
- * accepted by desktopGestureExclusionListener, it will be updated in the
- * appropriate classes.
+ * accepted by desktopGestureExclusionListener, it will be updated in the appropriate classes.
*/
fun updateTaskExclusionRegions(taskId: Int, taskExclusionRegions: Region) {
desktopExclusionRegions.put(taskId, taskExclusionRegions)
@@ -385,9 +362,9 @@
}
/**
- * Removes the desktop gesture exclusion region for the specified task; if exclusionRegion
- * has been accepted by desktopGestureExclusionListener, it will be updated in the
- * appropriate classes.
+ * Removes the desktop gesture exclusion region for the specified task; if exclusionRegion has
+ * been accepted by desktopGestureExclusionListener, it will be updated in the appropriate
+ * classes.
*/
fun removeExclusionRegion(taskId: Int) {
desktopExclusionRegions.delete(taskId)
@@ -396,16 +373,12 @@
}
}
- /**
- * Removes and returns the bounds saved before maximizing the given task.
- */
+ /** Removes and returns the bounds saved before maximizing the given task. */
fun removeBoundsBeforeMaximize(taskId: Int): Rect? {
return boundsBeforeMaximizeByTaskId.removeReturnOld(taskId)
}
- /**
- * Saves the bounds of the given task before maximizing.
- */
+ /** Saves the bounds of the given task before maximizing. */
fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) {
boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
}
@@ -414,7 +387,6 @@
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopModeTaskRepository")
dumpDisplayData(pw, innerPrefix)
- pw.println("${innerPrefix}freeformTasksInZOrder=${freeformTasksInZOrder.toDumpString()}")
pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
}
@@ -425,6 +397,9 @@
pw.println("${prefix}Display $displayId:")
pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
+ pw.println(
+ "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}"
+ )
}
}
@@ -432,9 +407,7 @@
* Defines interface for classes that can listen to changes for active tasks in desktop mode.
*/
interface ActiveTasksListener {
- /**
- * Called when the active tasks change in desktop mode.
- */
+ /** Called when the active tasks change in desktop mode. */
fun onActiveTasksChanged(displayId: Int) {}
}
@@ -442,9 +415,7 @@
* Defines interface for classes that can listen to changes for visible tasks in desktop mode.
*/
interface VisibleTasksListener {
- /**
- * Called when the desktop changes the number of visible freeform tasks.
- */
+ /** Called when the desktop changes the number of visible freeform tasks. */
fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index aa11a7d..a9d4e5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -24,11 +24,11 @@
import com.android.wm.shell.dagger.WMSingleton
import javax.inject.Inject
-/**
- * Log Aster UIEvents for desktop windowing mode.
- */
+/** Log Aster UIEvents for desktop windowing mode. */
@WMSingleton
-class DesktopModeUiEventLogger @Inject constructor(
+class DesktopModeUiEventLogger
+@Inject
+constructor(
private val mUiEventLogger: UiEventLogger,
private val mInstanceIdSequence: InstanceIdSequence
) {
@@ -47,16 +47,14 @@
mUiEventLogger.log(event, uid, packageName)
}
- /**
- * Retrieves a new instance id for a new interaction.
- */
+ /** Retrieves a new instance id for a new interaction. */
fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId()
/**
* Logs an event as part of a particular CUI, on a particular package.
*
* @param instanceId The id identifying an interaction, potentially taking place across multiple
- * surfaces. There should be a new id generated for each distinct CUI.
+ * surfaces. There should be a new id generated for each distinct CUI.
* @param uid The user id associated with the package the user is interacting with
* @param packageName The name of the package the user is interacting with
* @param event The event type to generate
@@ -75,20 +73,15 @@
}
companion object {
- /**
- * Enums for logging desktop windowing mode UiEvents.
- */
+ /** Enums for logging desktop windowing mode UiEvents. */
enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge")
DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721),
-
@UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner")
DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722),
-
@UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode")
DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723),
-
@UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode")
DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724);
@@ -97,4 +90,4 @@
private const val TAG = "DesktopModeUiEventLogger"
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 6da3741..217b1d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -28,13 +28,11 @@
import android.util.Size
import com.android.wm.shell.common.DisplayLayout
+val DESKTOP_MODE_INITIAL_BOUNDS_SCALE: Float =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
-val DESKTOP_MODE_INITIAL_BOUNDS_SCALE: Float = SystemProperties
- .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
-
-val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int = SystemProperties
- .getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
-
+val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
/**
* Calculates the initial bounds required for an application to fill a scale of the display bounds
@@ -52,51 +50,53 @@
val idealSize = calculateIdealSize(screenBounds, scale)
// If no top activity exists, apps fullscreen bounds and aspect ratio cannot be calculated.
// Instead default to the desired initial bounds.
- val topActivityInfo = taskInfo.topActivityInfo
- ?: return positionInScreen(idealSize, screenBounds)
+ val topActivityInfo =
+ taskInfo.topActivityInfo ?: return positionInScreen(idealSize, screenBounds)
- val initialSize: Size = when (taskInfo.configuration.orientation) {
- ORIENTATION_LANDSCAPE -> {
- if (taskInfo.isResizeable) {
- if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
- // Respect apps fullscreen width
- Size(taskInfo.appCompatTaskInfo.topActivityLetterboxWidth, idealSize.height)
+ val initialSize: Size =
+ when (taskInfo.configuration.orientation) {
+ ORIENTATION_LANDSCAPE -> {
+ if (taskInfo.isResizeable) {
+ if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
+ // Respect apps fullscreen width
+ Size(taskInfo.appCompatTaskInfo.topActivityLetterboxWidth, idealSize.height)
+ } else {
+ idealSize
+ }
} else {
- idealSize
- }
- } else {
- maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
- appAspectRatio)
- }
- }
- ORIENTATION_PORTRAIT -> {
- val customPortraitWidthForLandscapeApp = screenBounds.width() -
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
- if (taskInfo.isResizeable) {
- if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
- // Respect apps fullscreen height and apply custom app width
- Size(customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxHeight)
- } else {
- idealSize
- }
- } else {
- if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
- // Apply custom app width and calculate maximum size
- maximumSizeMaintainingAspectRatio(
- taskInfo,
- Size(customPortraitWidthForLandscapeApp, idealSize.height),
- appAspectRatio)
- } else {
- maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
- appAspectRatio)
+ maximumSizeMaintainingAspectRatio(taskInfo, idealSize, appAspectRatio)
}
}
+ ORIENTATION_PORTRAIT -> {
+ val customPortraitWidthForLandscapeApp =
+ screenBounds.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
+ if (taskInfo.isResizeable) {
+ if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ // Respect apps fullscreen height and apply custom app width
+ Size(
+ customPortraitWidthForLandscapeApp,
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
+ )
+ } else {
+ idealSize
+ }
+ } else {
+ if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ // Apply custom app width and calculate maximum size
+ maximumSizeMaintainingAspectRatio(
+ taskInfo,
+ Size(customPortraitWidthForLandscapeApp, idealSize.height),
+ appAspectRatio
+ )
+ } else {
+ maximumSizeMaintainingAspectRatio(taskInfo, idealSize, appAspectRatio)
+ }
+ }
+ }
+ else -> {
+ idealSize
+ }
}
- else -> {
- idealSize
- }
- }
return positionInScreen(initialSize, screenBounds)
}
@@ -136,19 +136,17 @@
return Size(finalWidth, finalHeight)
}
-/**
- * Calculates the aspect ratio of an activity from its fullscreen bounds.
- */
+/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
private fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth
val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
return maxOf(appLetterboxWidth, appLetterboxHeight) /
- minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
+ minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
}
val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
return maxOf(appBounds.height(), appBounds.width()) /
- minOf(appBounds.height(), appBounds.width()).toFloat()
+ minOf(appBounds.height(), appBounds.width()).toFloat()
}
/**
@@ -161,13 +159,15 @@
return Size(width, height)
}
-/**
- * Adjusts bounds to be positioned in the middle of the screen.
- */
+/** Adjusts bounds to be positioned in the middle of the screen. */
private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect {
// TODO(b/325240051): Position apps with bottom heavy offset
val heightOffset = (screenBounds.height() - desiredSize.height) / 2
val widthOffset = (screenBounds.width() - desiredSize.width) / 2
- return Rect(widthOffset, heightOffset,
- desiredSize.width + widthOffset, desiredSize.height + heightOffset)
+ return Rect(
+ widthOffset,
+ heightOffset,
+ desiredSize.width + widthOffset,
+ desiredSize.height + heightOffset
+ )
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 2e40ba7..38db1eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -17,10 +17,10 @@
package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -186,7 +186,7 @@
// In freeform, keep the top corners clear.
int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
? mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
+ com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
-captionHeight;
region.union(new Rect(0, transitionHeight, transitionEdgeWidth, layout.height()));
return region;
@@ -315,10 +315,11 @@
private static final float INDICATOR_FINAL_OPACITY = 0.35f;
private static final int MAXIMUM_OPACITY = 255;
- /** Determines how this animator will interact with the view's alpha:
- * Fade in, fade out, or no change to alpha
+ /**
+ * Determines how this animator will interact with the view's alpha:
+ * Fade in, fade out, or no change to alpha
*/
- private enum AlphaAnimType{
+ private enum AlphaAnimType {
ALPHA_FADE_IN_ANIM, ALPHA_FADE_OUT_ANIM, ALPHA_NO_CHANGE_ANIM
}
@@ -365,10 +366,10 @@
* Create animator for visual indicator changing type (i.e., fullscreen to freeform,
* freeform to split, etc.)
*
- * @param view the view for this indicator
+ * @param view the view for this indicator
* @param displayLayout information about the display the transitioning task is currently on
- * @param origType the original indicator type
- * @param newType the new indicator type
+ * @param origType the original indicator type
+ * @param newType the new indicator type
*/
private static VisualIndicatorAnimator animateIndicatorType(@NonNull View view,
@NonNull DisplayLayout displayLayout, IndicatorType origType,
@@ -469,7 +470,7 @@
*/
private static Rect getMaxBounds(Rect startBounds) {
return new Rect((int) (startBounds.left
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
(int) (startBounds.top
- (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
(int) (startBounds.right
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index e5bf53a..6e45397 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -97,69 +97,74 @@
/** Handles moving tasks in and out of desktop */
class DesktopTasksController(
- private val context: Context,
- shellInit: ShellInit,
- private val shellCommandHandler: ShellCommandHandler,
- private val shellController: ShellController,
- private val displayController: DisplayController,
- private val shellTaskOrganizer: ShellTaskOrganizer,
- private val syncQueue: SyncTransactionQueue,
- private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- private val dragAndDropController: DragAndDropController,
- private val transitions: Transitions,
- private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
- private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
- private val toggleResizeDesktopTaskTransitionHandler:
- ToggleResizeDesktopTaskTransitionHandler,
- private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
- private val desktopModeTaskRepository: DesktopModeTaskRepository,
- private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver,
- private val launchAdjacentController: LaunchAdjacentController,
- private val recentsTransitionHandler: RecentsTransitionHandler,
- private val multiInstanceHelper: MultiInstanceHelper,
- @ShellMainThread private val mainExecutor: ShellExecutor,
- private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
-) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler,
+ private val context: Context,
+ shellInit: ShellInit,
+ private val shellCommandHandler: ShellCommandHandler,
+ private val shellController: ShellController,
+ private val displayController: DisplayController,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val dragAndDropController: DragAndDropController,
+ private val transitions: Transitions,
+ private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
+ private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
+ private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
+ private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
+ private val desktopModeTaskRepository: DesktopModeTaskRepository,
+ private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver,
+ private val launchAdjacentController: LaunchAdjacentController,
+ private val recentsTransitionHandler: RecentsTransitionHandler,
+ private val multiInstanceHelper: MultiInstanceHelper,
+ @ShellMainThread private val mainExecutor: ShellExecutor,
+ private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
+) :
+ RemoteCallable<DesktopTasksController>,
+ Transitions.TransitionHandler,
DragAndDropController.DragAndDropListener {
private val desktopMode: DesktopModeImpl
private var visualIndicator: DesktopModeVisualIndicator? = null
private val desktopModeShellCommandHandler: DesktopModeShellCommandHandler =
DesktopModeShellCommandHandler(this)
- private val mOnAnimationFinishedCallback = Consumer<SurfaceControl.Transaction> {
- t: SurfaceControl.Transaction ->
- visualIndicator?.releaseVisualIndicator(t)
- visualIndicator = null
- }
- private val taskVisibilityListener = object : VisibleTasksListener {
- override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
- launchAdjacentController.launchAdjacentEnabled = visibleTasksCount == 0
- }
- }
- private val dragToDesktopStateListener = object : DragToDesktopStateListener {
- override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
- removeVisualIndicator(tx)
- }
-
- override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) {
- removeVisualIndicator(tx)
- }
-
- private fun removeVisualIndicator(tx: SurfaceControl.Transaction) {
- visualIndicator?.releaseVisualIndicator(tx)
+ private val mOnAnimationFinishedCallback =
+ Consumer<SurfaceControl.Transaction> { t: SurfaceControl.Transaction ->
+ visualIndicator?.releaseVisualIndicator(t)
visualIndicator = null
}
- }
+ private val taskVisibilityListener =
+ object : VisibleTasksListener {
+ override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
+ launchAdjacentController.launchAdjacentEnabled = visibleTasksCount == 0
+ }
+ }
+ private val dragToDesktopStateListener =
+ object : DragToDesktopStateListener {
+ override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ private fun removeVisualIndicator(tx: SurfaceControl.Transaction) {
+ visualIndicator?.releaseVisualIndicator(tx)
+ visualIndicator = null
+ }
+ }
private val transitionAreaHeight
- get() = context.resources.getDimensionPixelSize(
+ get() =
+ context.resources.getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height
- )
+ )
private val transitionAreaWidth
- get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
- )
+ get() =
+ context.resources.getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
+ )
/** Task id of the task currently being dragged from fullscreen/split. */
val draggingTaskId
@@ -178,11 +183,7 @@
private fun onInit() {
KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController")
shellCommandHandler.addDumpCallback(this::dump, this)
- shellCommandHandler.addCommandCallback(
- "desktopmode",
- desktopModeShellCommandHandler,
- this
- )
+ shellCommandHandler.addCommandCallback("desktopmode", desktopModeShellCommandHandler, this)
shellController.addExternalInterface(
ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE,
{ createExternalInterface() },
@@ -232,9 +233,10 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
// TODO(b/309014605): ensure remote transition is supplied once state is introduced
val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
- val handler = remoteTransition?.let {
- OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
- }
+ val handler =
+ remoteTransition?.let {
+ OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
+ }
transitions.startTransition(transitionType, wct, handler).also { t ->
handler?.setTransition(t)
}
@@ -253,9 +255,9 @@
val allFocusedTasks =
shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo ->
taskInfo.isFocused &&
- (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
- taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
- taskInfo.activityType != ACTIVITY_TYPE_HOME
+ (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+ taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
+ taskInfo.activityType != ACTIVITY_TYPE_HOME
}
if (allFocusedTasks.isNotEmpty()) {
when (allFocusedTasks.size) {
@@ -278,7 +280,7 @@
KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: Cannot enter desktop, expected less " +
- "than 3 focused tasks but found %d",
+ "than 3 focused tasks but found %d",
allFocusedTasks.size
)
}
@@ -288,27 +290,24 @@
/** Move a task with given `taskId` to desktop */
fun moveToDesktop(
- taskId: Int,
- wct: WindowContainerTransaction = WindowContainerTransaction()
+ taskId: Int,
+ wct: WindowContainerTransaction = WindowContainerTransaction()
): Boolean {
- shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
- task -> moveToDesktop(task, wct)
- } ?: return false
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task, wct) }
+ ?: return false
return true
}
- /**
- * Move a task to desktop
- */
+ /** Move a task to desktop */
fun moveToDesktop(
- task: RunningTaskInfo,
- wct: WindowContainerTransaction = WindowContainerTransaction()
+ task: RunningTaskInfo,
+ wct: WindowContainerTransaction = WindowContainerTransaction()
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: Cannot enter desktop, " +
- "display does not meet minimum size requirements"
+ "display does not meet minimum size requirements"
)
return
}
@@ -316,7 +315,7 @@
KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: Cannot enter desktop, " +
- "translucent top activity found. This is likely a modal dialog."
+ "translucent top activity found. This is likely a modal dialog."
)
return
}
@@ -328,7 +327,7 @@
exitSplitIfApplicable(wct, task)
// Bring other apps to front first
val taskToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
+ bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -340,21 +339,21 @@
}
/**
- * The first part of the animated drag to desktop transition. This is
- * followed with a call to [finalizeDragToDesktop] or [cancelDragToDesktop].
+ * The first part of the animated drag to desktop transition. This is followed with a call to
+ * [finalizeDragToDesktop] or [cancelDragToDesktop].
*/
fun startDragToDesktop(
- taskInfo: RunningTaskInfo,
- dragToDesktopValueAnimator: MoveToDesktopAnimator,
+ taskInfo: RunningTaskInfo,
+ dragToDesktopValueAnimator: MoveToDesktopAnimator,
) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: startDragToDesktop taskId=%d",
- taskInfo.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: startDragToDesktop taskId=%d",
+ taskInfo.taskId
)
dragToDesktopTransitionHandler.startDragToDesktopTransition(
- taskInfo.taskId,
- dragToDesktopValueAnimator
+ taskInfo.taskId,
+ dragToDesktopValueAnimator
)
}
@@ -364,16 +363,15 @@
*/
private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: finalizeDragToDesktop taskId=%d",
- taskInfo.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: finalizeDragToDesktop taskId=%d",
+ taskInfo.taskId
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
moveHomeTaskToFront(wct)
val taskToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(
- taskInfo.displayId, wct, taskInfo.taskId)
+ bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
@@ -381,9 +379,9 @@
}
/**
- * Perform needed cleanup transaction once animation is complete. Bounds need to be set
- * here instead of initial wct to both avoid flicker and to have task bounds to use for
- * the staging animation.
+ * Perform needed cleanup transaction once animation is complete. Bounds need to be set here
+ * instead of initial wct to both avoid flicker and to have task bounds to use for the staging
+ * animation.
*
* @param taskInfo task entering split that requires a bounds update
*/
@@ -395,16 +393,13 @@
}
/**
- * Perform clean up of the desktop wallpaper activity if the closed window task is
- * the last active task.
+ * Perform clean up of the desktop wallpaper activity if the closed window task is the last
+ * active task.
*
* @param wct transaction to modify if the last active task is closed
* @param taskId task id of the window that's being closed
*/
- fun onDesktopWindowClose(
- wct: WindowContainerTransaction,
- taskId: Int
- ) {
+ fun onDesktopWindowClose(wct: WindowContainerTransaction, taskId: Int) {
if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) {
removeWallpaperActivity(wct)
}
@@ -419,8 +414,9 @@
/** Enter fullscreen by moving the focused freeform task in given `displayId` to fullscreen. */
fun enterFullscreen(displayId: Int) {
- getFocusedFreeformTask(displayId)
- ?.let { moveToFullscreenWithAnimation(it, it.positionInParent) }
+ getFocusedFreeformTask(displayId)?.let {
+ moveToFullscreenWithAnimation(it, it.positionInParent)
+ }
}
/** Move a desktop app to split screen. */
@@ -449,8 +445,7 @@
splitScreenController.getStageOfTask(taskInfo.taskId),
EXIT_REASON_DESKTOP_MODE
)
- splitScreenController.transitionHandler
- ?.onSplitToDesktop()
+ splitScreenController.transitionHandler?.onSplitToDesktop()
}
}
@@ -469,16 +464,16 @@
private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: moveToFullscreen with animation taskId=%d",
- task.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: moveToFullscreen with animation taskId=%d",
+ task.taskId
)
val wct = WindowContainerTransaction()
addMoveToFullscreenChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
exitDesktopTaskTransitionHandler.startTransition(
- Transitions.TRANSIT_EXIT_DESKTOP_MODE,
+ Transitions.TRANSIT_EXIT_DESKTOP_MODE,
wct,
position,
mOnAnimationFinishedCallback
@@ -517,12 +512,12 @@
* Move task to the next display.
*
* Queries all current known display ids and sorts them in ascending order. Then iterates
- * through the list and looks for the display id that is larger than the display id for
- * the passed in task. If a display with a higher id is not found, iterates through the list and
+ * through the list and looks for the display id that is larger than the display id for the
+ * passed in task. If a display with a higher id is not found, iterates through the list and
* finds the first display id that is not the display id for the passed in task.
*
- * If a display matching the above criteria is found, re-parents the task to that display.
- * No-op if no such display is found.
+ * If a display matching the above criteria is found, re-parents the task to that display. No-op
+ * if no such display is found.
*/
fun moveToNextDisplay(taskId: Int) {
val task = shellTaskOrganizer.getRunningTaskInfo(taskId)
@@ -533,7 +528,7 @@
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"moveToNextDisplay: taskId=%d taskDisplayId=%d",
- taskId,
+ taskId,
task.displayId
)
@@ -560,7 +555,7 @@
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"moveToDisplay: taskId=%d displayId=%d",
- task.taskId,
+ task.taskId,
displayId
)
@@ -585,9 +580,9 @@
}
/**
- * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the
- * stable bounds) and a free floating state (either the last saved bounds if available or the
- * default bounds otherwise).
+ * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the stable
+ * bounds) and a free floating state (either the last saved bounds if available or the default
+ * bounds otherwise).
*/
fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
@@ -600,11 +595,11 @@
// before the task was toggled to stable bounds were saved, toggle the task to those
// bounds. Otherwise, toggle to the default bounds.
val taskBoundsBeforeMaximize =
- desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
+ desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
if (taskBoundsBeforeMaximize != null) {
destinationBounds.set(taskBoundsBeforeMaximize)
} else {
- if (Flags.enableWindowingDynamicInitialBounds()){
+ if (Flags.enableWindowingDynamicInitialBounds()) {
destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
} else {
destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
@@ -650,8 +645,12 @@
val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
val heightOffset = (displayLayout.height() - desiredHeight) / 2
val widthOffset = (displayLayout.width() - desiredWidth) / 2
- return Rect(widthOffset, heightOffset,
- desiredWidth + widthOffset, desiredHeight + heightOffset)
+ return Rect(
+ widthOffset,
+ heightOffset,
+ desiredWidth + widthOffset,
+ desiredHeight + heightOffset
+ )
}
private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
@@ -693,19 +692,21 @@
}
private fun bringDesktopAppsToFrontBeforeShowingNewTask(
- displayId: Int,
- wct: WindowContainerTransaction,
- newTaskIdInFront: Int
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskIdInFront: Int
): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
private fun bringDesktopAppsToFront(
- displayId: Int,
- wct: WindowContainerTransaction,
- newTaskIdInFront: Int? = null
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskIdInFront: Int? = null
): RunningTaskInfo? {
- KtProtoLog.v(WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: bringDesktopAppsToFront, newTaskIdInFront=%s",
- newTaskIdInFront ?: "null")
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: bringDesktopAppsToFront, newTaskIdInFront=%s",
+ newTaskIdInFront ?: "null"
+ )
if (Flags.enableDesktopWindowingWallpaperActivity()) {
// Add translucent wallpaper activity to show the wallpaper underneath
@@ -716,19 +717,25 @@
}
val nonMinimizedTasksOrderedFrontToBack =
- desktopModeTaskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId)
+ desktopModeTaskRepository.getActiveNonMinimizedTasksOrderedFrontToBack(displayId)
// If we're adding a new Task we might need to minimize an old one
val taskToMinimize: RunningTaskInfo? =
- if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
- desktopTasksLimiter.get().getTaskToMinimizeIfNeeded(
- nonMinimizedTasksOrderedFrontToBack, newTaskIdInFront)
- } else { null }
+ if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
+ desktopTasksLimiter
+ .get()
+ .getTaskToMinimizeIfNeeded(
+ nonMinimizedTasksOrderedFrontToBack,
+ newTaskIdInFront
+ )
+ } else {
+ null
+ }
nonMinimizedTasksOrderedFrontToBack
- // If there is a Task to minimize, let it stay behind the Home Task
- .filter { taskId -> taskId != taskToMinimize?.taskId }
- .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
- .reversed() // Start from the back so the front task is brought forward last
- .forEach { task -> wct.reorder(task.token, true /* onTop */) }
+ // If there is a Task to minimize, let it stay behind the Home Task
+ .filter { taskId -> taskId != taskToMinimize?.taskId }
+ .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
+ .reversed() // Start from the back so the front task is brought forward last
+ .forEach { task -> wct.reorder(task.token, true /* onTop */) }
return taskToMinimize
}
@@ -742,13 +749,19 @@
private fun addWallpaperActivity(wct: WindowContainerTransaction) {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper")
val intent = Intent(context, DesktopWallpaperActivity::class.java)
- val options = ActivityOptions.makeBasic().apply {
- isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
- pendingIntentBackgroundActivityStartMode =
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- }
- val pendingIntent = PendingIntent.getActivity(context, /* requestCode = */ 0, intent,
- PendingIntent.FLAG_IMMUTABLE)
+ val options =
+ ActivityOptions.makeBasic().apply {
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ pendingIntentBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ }
+ val pendingIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode = */ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
}
@@ -807,8 +820,7 @@
false
}
// Handle back navigation for the last window if wallpaper available
- shouldRemoveWallpaper(request) ->
- true
+ shouldRemoveWallpaper(request) -> true
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
reason = "transition type not handled (${request.type})"
@@ -826,7 +838,7 @@
}
// Only handle fullscreen or freeform tasks
triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
+ triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
reason = "windowingMode not handled (${triggerTask.windowingMode})"
false
}
@@ -836,31 +848,32 @@
if (!shouldHandleRequest) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: skipping handleRequest reason=%s",
- reason
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: skipping handleRequest reason=%s",
+ reason
)
return null
}
- val result = triggerTask?.let { task ->
- when {
- request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
- // Check if the task has a top transparent activity
- shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
- // Check if fullscreen task should be updated
- task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
- // Check if freeform task should be updated
- task.isFreeform -> handleFreeformTaskLaunch(task, transition)
- else -> {
- null
+ val result =
+ triggerTask?.let { task ->
+ when {
+ request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
+ // Check if the task has a top transparent activity
+ shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
+ // Check if fullscreen task should be updated
+ task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
+ // Check if freeform task should be updated
+ task.isFreeform -> handleFreeformTaskLaunch(task, transition)
+ else -> {
+ null
+ }
}
}
- }
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: handleRequest result=%s",
- result ?: "null"
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: handleRequest result=%s",
+ result ?: "null"
)
return result
}
@@ -870,18 +883,15 @@
* This is intended to be used when desktop mode is part of another animation but isn't, itself,
* animating.
*/
- fun syncSurfaceState(
- info: TransitionInfo,
- finishTransaction: SurfaceControl.Transaction
- ) {
+ fun syncSurfaceState(info: TransitionInfo, finishTransaction: SurfaceControl.Transaction) {
// Add rounded corners to freeform windows
if (!DesktopModeStatus.useRoundedCorners()) {
return
}
val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
info.changes
- .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
- .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
+ .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
+ .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
}
private fun shouldLaunchAsModal(task: TaskInfo) =
@@ -889,23 +899,23 @@
private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
return Flags.enableDesktopWindowingWallpaperActivity() &&
- request.type == TRANSIT_TO_BACK &&
- request.triggerTask?.let { task ->
- desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
- } ?: false
+ request.type == TRANSIT_TO_BACK &&
+ request.triggerTask?.let { task ->
+ desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
+ } ?: false
}
private fun handleFreeformTaskLaunch(
- task: RunningTaskInfo,
- transition: IBinder
+ task: RunningTaskInfo,
+ transition: IBinder
): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
if (!desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch freeform task to fullscreen oon transition" +
- " taskId=%d",
- task.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: switch freeform task to fullscreen oon transition" +
+ " taskId=%d",
+ task.taskId
)
return WindowContainerTransaction().also { wct ->
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
@@ -927,16 +937,16 @@
}
private fun handleFullscreenTaskLaunch(
- task: RunningTaskInfo,
- transition: IBinder
+ task: RunningTaskInfo,
+ transition: IBinder
): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
if (desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch fullscreen task to freeform on transition" +
- " taskId=%d",
- task.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: switch fullscreen task to freeform on transition" +
+ " taskId=%d",
+ task.taskId
)
return WindowContainerTransaction().also { wct ->
addMoveToDesktopChanges(wct, task)
@@ -952,21 +962,18 @@
// Always launch transparent tasks in fullscreen.
private fun handleTransparentTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
// Already fullscreen, no-op.
- if (task.isFullscreen)
- return null
- return WindowContainerTransaction().also { wct ->
- addMoveToFullscreenChanges(wct, task)
- }
+ if (task.isFullscreen) return null
+ return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task) }
}
/** Handle back navigation by removing wallpaper activity if it's the last active task */
private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? {
- if (desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
- desktopModeTaskRepository.wallpaperActivityToken != null) {
+ if (
+ desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
+ desktopModeTaskRepository.wallpaperActivityToken != null
+ ) {
// Remove wallpaper activity when the last active task is removed
- return WindowContainerTransaction().also { wct ->
- removeWallpaperActivity(wct)
- }
+ return WindowContainerTransaction().also { wct -> removeWallpaperActivity(wct) }
} else {
return null
}
@@ -979,12 +986,13 @@
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
- val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
- // Display windowing is freeform, set to undefined and inherit it
- WINDOWING_MODE_UNDEFINED
- } else {
- WINDOWING_MODE_FREEFORM
- }
+ val targetWindowingMode =
+ if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
+ // Display windowing is freeform, set to undefined and inherit it
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FREEFORM
+ }
if (Flags.enableWindowingDynamicInitialBounds()) {
wct.setBounds(taskInfo.token, calculateInitialBounds(displayLayout, taskInfo))
}
@@ -1001,12 +1009,13 @@
) {
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
- val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- // Display windowing is fullscreen, set to undefined and inherit it
- WINDOWING_MODE_UNDEFINED
- } else {
- WINDOWING_MODE_FULLSCREEN
- }
+ val targetWindowingMode =
+ if (tdaWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Display windowing is fullscreen, set to undefined and inherit it
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FULLSCREEN
+ }
wct.setWindowingMode(taskInfo.token, targetWindowingMode)
wct.setBounds(taskInfo.token, Rect())
if (isDesktopDensityOverrideSet()) {
@@ -1018,10 +1027,7 @@
* Adds split screen changes to a transaction. Note that bounds are not reset here due to
* animation; see {@link onDesktopSplitSelectAnimComplete}
*/
- private fun addMoveToSplitChanges(
- wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo
- ) {
+ private fun addMoveToSplitChanges(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
// This windowing mode is to get the transition animation started; once we complete
// split select, we will change windowing mode to undefined and inherit from split stage.
// Going to undefined here causes task to flicker to the top left.
@@ -1034,38 +1040,35 @@
/** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
private fun addAndGetMinimizeChangesIfNeeded(
- displayId: Int,
- wct: WindowContainerTransaction,
- newTaskInfo: RunningTaskInfo
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newTaskInfo: RunningTaskInfo
): RunningTaskInfo? {
if (!desktopTasksLimiter.isPresent) return null
- return desktopTasksLimiter.get().addAndGetMinimizeTaskChangesIfNeeded(
- displayId, wct, newTaskInfo)
+ return desktopTasksLimiter
+ .get()
+ .addAndGetMinimizeTaskChangesIfNeeded(displayId, wct, newTaskInfo)
}
private fun addPendingMinimizeTransition(
- transition: IBinder,
- taskToMinimize: RunningTaskInfo?
+ transition: IBinder,
+ taskToMinimize: RunningTaskInfo?
) {
if (taskToMinimize == null) return
desktopTasksLimiter.ifPresent {
- it.addPendingMinimizeChange(
- transition, taskToMinimize.displayId, taskToMinimize.taskId)
+ it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId)
}
}
/** Enter split by using the focused desktop task in given `displayId`. */
- fun enterSplit(
- displayId: Int,
- leftOrTop: Boolean
- ) {
+ fun enterSplit(displayId: Int, leftOrTop: Boolean) {
getFocusedFreeformTask(displayId)?.let { requestSplit(it, leftOrTop) }
}
private fun getFocusedFreeformTask(displayId: Int): RunningTaskInfo? {
- return shellTaskOrganizer.getRunningTasks(displayId)
- .find { taskInfo -> taskInfo.isFocused &&
- taskInfo.windowingMode == WINDOWING_MODE_FREEFORM }
+ return shellTaskOrganizer.getRunningTasks(displayId).find { taskInfo ->
+ taskInfo.isFocused && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+ }
}
/**
@@ -1078,7 +1081,8 @@
leftOrTop: Boolean = false,
) {
val windowingMode = taskInfo.windowingMode
- if (windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_FREEFORM
+ if (
+ windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_FREEFORM
) {
val wct = WindowContainerTransaction()
addMoveToSplitChanges(wct, taskInfo)
@@ -1107,9 +1111,9 @@
/**
* Perform checks required on drag move. Create/release fullscreen indicator as needed.
- * Different sources for x and y coordinates are used due to different needs for each:
- * We want split transitions to be based on input coordinates but fullscreen transition
- * to be based on task edge coordinate.
+ * Different sources for x and y coordinates are used due to different needs for each: We want
+ * split transitions to be based on input coordinates but fullscreen transition to be based on
+ * task edge coordinate.
*
* @param taskInfo the task being dragged.
* @param taskSurface SurfaceControl of dragged task.
@@ -1133,9 +1137,16 @@
taskTop: Float
): DesktopModeVisualIndicator.IndicatorType {
// If the visual indicator does not exist, create it.
- val indicator = visualIndicator ?: DesktopModeVisualIndicator(
- syncQueue, taskInfo, displayController, context, taskSurface,
- rootTaskDisplayAreaOrganizer)
+ val indicator =
+ visualIndicator
+ ?: DesktopModeVisualIndicator(
+ syncQueue,
+ taskInfo,
+ displayController,
+ context,
+ taskSurface,
+ rootTaskDisplayAreaOrganizer
+ )
if (visualIndicator == null) visualIndicator = indicator
return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
}
@@ -1161,10 +1172,11 @@
}
val indicator = visualIndicator ?: return
- val indicatorType = indicator.updateIndicatorType(
- PointF(inputCoordinate.x, taskBounds.top.toFloat()),
- taskInfo.windowingMode
- )
+ val indicatorType =
+ indicator.updateIndicatorType(
+ PointF(inputCoordinate.x, taskBounds.top.toFloat()),
+ taskInfo.windowingMode
+ )
when (indicatorType) {
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
moveToFullscreenWithAnimation(taskInfo, position)
@@ -1180,8 +1192,12 @@
DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> {
// If task bounds are outside valid drag area, snap them inward and perform a
// transaction to set bounds.
- if (DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
- taskBounds, validDragArea)) {
+ if (
+ DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
+ taskBounds,
+ validDragArea
+ )
+ ) {
val wct = WindowContainerTransaction()
wct.setBounds(taskInfo.token, taskBounds)
transitions.startTransition(TRANSIT_CHANGE, wct, null)
@@ -1189,8 +1205,9 @@
releaseVisualIndicator()
}
DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
- throw IllegalArgumentException("Should not be receiving TO_DESKTOP_INDICATOR for " +
- "a freeform task.")
+ throw IllegalArgumentException(
+ "Should not be receiving TO_DESKTOP_INDICATOR for " + "a freeform task."
+ )
}
}
// A freeform drag-move ended, remove the indicator immediately.
@@ -1205,8 +1222,7 @@
*/
fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) {
val indicator = getVisualIndicator() ?: return
- val indicatorType = indicator
- .updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
+ val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
when (indicatorType) {
DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
@@ -1229,16 +1245,12 @@
}
}
- /**
- * Update the exclusion region for a specified task
- */
+ /** Update the exclusion region for a specified task */
fun onExclusionRegionChanged(taskId: Int, exclusionRegion: Region) {
desktopModeTaskRepository.updateTaskExclusionRegions(taskId, exclusionRegion)
}
- /**
- * Remove a previously tracked exclusion region for a specified task.
- */
+ /** Remove a previously tracked exclusion region for a specified task. */
fun removeExclusionRegionForTask(taskId: Int) {
desktopModeTaskRepository.removeExclusionRegion(taskId)
}
@@ -1259,10 +1271,7 @@
* @param listener the listener to add.
* @param callbackExecutor the executor to call the listener on.
*/
- fun setTaskRegionListener(
- listener: Consumer<Region>,
- callbackExecutor: Executor
- ) {
+ fun setTaskRegionListener(listener: Consumer<Region>, callbackExecutor: Executor) {
desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor)
}
@@ -1287,15 +1296,16 @@
}
// Start a new transition to launch the app
- val opts = ActivityOptions.makeBasic().apply {
- launchWindowingMode = WINDOWING_MODE_FREEFORM
- pendingIntentLaunchFlags =
- Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
- )
- isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
- }
+ val opts =
+ ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FREEFORM
+ pendingIntentLaunchFlags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ )
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ }
val wct = WindowContainerTransaction()
wct.sendPendingIntent(launchIntent, null, opts.toBundle())
transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */)
@@ -1321,8 +1331,8 @@
@ExternalThread
private inner class DesktopModeImpl : DesktopMode {
override fun addVisibleTasksListener(
- listener: VisibleTasksListener,
- callbackExecutor: Executor
+ listener: VisibleTasksListener,
+ callbackExecutor: Executor
) {
mainExecutor.execute {
this@DesktopTasksController.addVisibleTasksListener(listener, callbackExecutor)
@@ -1330,8 +1340,8 @@
}
override fun addDesktopGestureExclusionRegionListener(
- listener: Consumer<Region>,
- callbackExecutor: Executor
+ listener: Consumer<Region>,
+ callbackExecutor: Executor
) {
mainExecutor.execute {
this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)
@@ -1339,21 +1349,15 @@
}
override fun moveFocusedTaskToDesktop(displayId: Int) {
- mainExecutor.execute {
- this@DesktopTasksController.moveFocusedTaskToDesktop(displayId)
- }
+ mainExecutor.execute { this@DesktopTasksController.moveFocusedTaskToDesktop(displayId) }
}
override fun moveFocusedTaskToFullscreen(displayId: Int) {
- mainExecutor.execute {
- this@DesktopTasksController.enterFullscreen(displayId)
- }
+ mainExecutor.execute { this@DesktopTasksController.enterFullscreen(displayId) }
}
override fun moveFocusedTaskToStageSplit(displayId: Int, leftOrTop: Boolean) {
- mainExecutor.execute {
- this@DesktopTasksController.enterSplit(displayId, leftOrTop)
- }
+ mainExecutor.execute { this@DesktopTasksController.enterSplit(displayId, leftOrTop) }
}
}
@@ -1363,36 +1367,35 @@
IDesktopMode.Stub(), ExternalInterfaceBinder {
private lateinit var remoteListener:
- SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>
+ SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>
- private val listener: VisibleTasksListener = object : VisibleTasksListener {
- override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
- KtProtoLog.v(
+ private val listener: VisibleTasksListener =
+ object : VisibleTasksListener {
+ override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
+ KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"IDesktopModeImpl: onVisibilityChanged display=%d visible=%d",
displayId,
visibleTasksCount
- )
- remoteListener.call {
- l -> l.onTasksVisibilityChanged(displayId, visibleTasksCount)
+ )
+ remoteListener.call { l ->
+ l.onTasksVisibilityChanged(displayId, visibleTasksCount)
+ }
}
}
- }
init {
remoteListener =
- SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
- controller,
- { c ->
- c.desktopModeTaskRepository.addVisibleTasksListener(
- listener,
- c.mainExecutor
- )
- },
- { c ->
- c.desktopModeTaskRepository.removeVisibleTasksListener(listener)
- }
- )
+ SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
+ controller,
+ { c ->
+ c.desktopModeTaskRepository.addVisibleTasksListener(
+ listener,
+ c.mainExecutor
+ )
+ },
+ { c -> c.desktopModeTaskRepository.removeVisibleTasksListener(listener) }
+ )
}
/** Invalidates this instance, preventing future calls from updating the controller. */
@@ -1402,24 +1405,19 @@
}
override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "showDesktopApps"
- ) { c -> c.showDesktopApps(displayId, remoteTransition) }
+ ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
+ c.showDesktopApps(displayId, remoteTransition)
+ }
}
override fun showDesktopApp(taskId: Int) {
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "showDesktopApp"
- ) { c -> c.moveTaskToFront(taskId) }
+ ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c ->
+ c.moveTaskToFront(taskId)
+ }
}
override fun stashDesktopApps(displayId: Int) {
- KtProtoLog.w(
- WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: stashDesktopApps is deprecated"
- )
+ KtProtoLog.w(WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: stashDesktopApps is deprecated")
}
override fun hideStashedDesktopApps(displayId: Int) {
@@ -1444,35 +1442,38 @@
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
"onDesktopSplitSelectAnimComplete"
- ) { c -> c.onDesktopSplitSelectAnimComplete(taskInfo) }
+ ) { c ->
+ c.onDesktopSplitSelectAnimComplete(taskInfo)
+ }
}
override fun setTaskListener(listener: IDesktopTaskListener?) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: set task listener=%s",
- listener ?: "null"
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: set task listener=%s",
+ listener ?: "null"
)
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "setTaskListener"
- ) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() }
+ ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ ->
+ listener?.let { remoteListener.register(it) } ?: remoteListener.unregister()
+ }
}
override fun moveToDesktop(taskId: Int) {
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "moveToDesktop"
- ) { c -> c.moveToDesktop(taskId) }
+ ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c ->
+ c.moveToDesktop(taskId)
+ }
}
}
companion object {
@JvmField
- val DESKTOP_MODE_INITIAL_BOUNDS_SCALE = SystemProperties
- .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
+ val DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
}
/** The positions on a screen that a task can snap to. */
- enum class SnapPosition { RIGHT, LEFT }
+ enum class SnapPosition {
+ RIGHT,
+ LEFT
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index e5e435d..98c79d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -50,26 +50,25 @@
* gesture.
*/
class DragToDesktopTransitionHandler(
- private val context: Context,
- private val transitions: Transitions,
- private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+ private val context: Context,
+ private val transitions: Transitions,
+ private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>
) : TransitionHandler {
constructor(
- context: Context,
- transitions: Transitions,
- rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ context: Context,
+ transitions: Transitions,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
) : this(
- context,
- transitions,
- rootTaskDisplayAreaOrganizer,
- Supplier { SurfaceControl.Transaction() }
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ Supplier { SurfaceControl.Transaction() }
)
private val rectEvaluator = RectEvaluator(Rect())
- private val launchHomeIntent = Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
+ private val launchHomeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
private var dragToDesktopStateListener: DragToDesktopStateListener? = null
private lateinit var splitScreenController: SplitScreenController
@@ -107,52 +106,55 @@
* after one of the "end" or "cancel" transitions is merged into this transition.
*/
fun startDragToDesktopTransition(
- taskId: Int,
- dragToDesktopAnimator: MoveToDesktopAnimator,
+ taskId: Int,
+ dragToDesktopAnimator: MoveToDesktopAnimator,
) {
if (inProgress) {
KtProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
- "DragToDesktop: Drag to desktop transition already in progress."
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DragToDesktop: Drag to desktop transition already in progress."
)
return
}
- val options = ActivityOptions.makeBasic().apply {
- setTransientLaunch()
- setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
- pendingIntentCreatorBackgroundActivityStartMode =
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- }
- val pendingIntent = PendingIntent.getActivity(
+ val options =
+ ActivityOptions.makeBasic().apply {
+ setTransientLaunch()
+ setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
+ pendingIntentCreatorBackgroundActivityStartMode =
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ }
+ val pendingIntent =
+ PendingIntent.getActivity(
context,
0 /* requestCode */,
launchHomeIntent,
FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
options.toBundle()
- )
+ )
val wct = WindowContainerTransaction()
wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
- val startTransitionToken = transitions
- .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
+ val startTransitionToken =
+ transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
- transitionState = if (isSplitTask(taskId)) {
- val otherTask = getOtherSplitTask(taskId) ?: throw IllegalStateException(
- "Expected split task to have a counterpart."
- )
- TransitionState.FromSplit(
+ transitionState =
+ if (isSplitTask(taskId)) {
+ val otherTask =
+ getOtherSplitTask(taskId)
+ ?: throw IllegalStateException("Expected split task to have a counterpart.")
+ TransitionState.FromSplit(
draggedTaskId = taskId,
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
otherSplitTask = otherTask
- )
- } else {
- TransitionState.FromFullscreen(
+ )
+ } else {
+ TransitionState.FromFullscreen(
draggedTaskId = taskId,
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken
- )
- }
+ )
+ }
}
/**
@@ -216,15 +218,16 @@
}
override fun startAnimation(
- transition: IBinder,
- info: TransitionInfo,
- startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback
): Boolean {
val state = requireTransitionState()
- val isStartDragToDesktop = info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
+ val isStartDragToDesktop =
+ info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
transition == state.startTransitionToken
if (!isStartDragToDesktop) {
return false
@@ -257,13 +260,16 @@
when (state) {
is TransitionState.FromSplit -> {
state.splitRootChange = change
- val layer = if (!state.cancelled) {
- // Normal case, split root goes to the bottom behind everything else.
- appLayers - i
- } else {
- // Cancel-early case, pretend nothing happened so split root stays top.
- dragLayer
- }
+ val layer =
+ if (!state.cancelled) {
+ // Normal case, split root goes to the bottom behind everything
+ // else.
+ appLayers - i
+ } else {
+ // Cancel-early case, pretend nothing happened so split root stays
+ // top.
+ dragLayer
+ }
startTransaction.apply {
setLayer(change.leash, layer)
show(change.leash)
@@ -308,7 +314,10 @@
if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) {
state.draggedTaskChange = change
taskDisplayAreaOrganizer.reparentToDisplayArea(
- change.endDisplayId, change.leash, startTransaction)
+ change.endDisplayId,
+ change.leash,
+ startTransaction
+ )
val bounds = change.endAbsBounds
startTransaction.apply {
setLayer(change.leash, dragLayer)
@@ -339,28 +348,34 @@
}
override fun mergeAnimation(
- transition: IBinder,
- info: TransitionInfo,
- t: SurfaceControl.Transaction,
- mergeTarget: IBinder,
- finishCallback: Transitions.TransitionFinishCallback
+ transition: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ mergeTarget: IBinder,
+ finishCallback: Transitions.TransitionFinishCallback
) {
val state = requireTransitionState()
- val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
+ val isCancelTransition =
+ info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
transition == state.cancelTransitionToken &&
mergeTarget == state.startTransitionToken
- val isEndTransition = info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
+ val isEndTransition =
+ info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
mergeTarget == state.startTransitionToken
- val startTransactionFinishT = state.startTransitionFinishTransaction
+ val startTransactionFinishT =
+ state.startTransitionFinishTransaction
?: error("Start transition expected to be waiting for merge but wasn't")
- val startTransitionFinishCb = state.startTransitionFinishCb
+ val startTransitionFinishCb =
+ state.startTransitionFinishCb
?: error("Start transition expected to be waiting for merge but wasn't")
if (isEndTransition) {
info.changes.withIndex().forEach { (i, change) ->
// If we're exiting split, hide the remaining split task.
- if (state is TransitionState.FromSplit &&
- change.taskInfo?.taskId == state.otherSplitTask) {
+ if (
+ state is TransitionState.FromSplit &&
+ change.taskInfo?.taskId == state.otherSplitTask
+ ) {
t.hide(change.leash)
startTransactionFinishT.hide(change.leash)
}
@@ -373,14 +388,16 @@
state.draggedTaskChange = change
} else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) {
// Other freeform tasks that are being restored go behind the dragged task.
- val draggedTaskLeash = state.draggedTaskChange?.leash
+ val draggedTaskLeash =
+ state.draggedTaskChange?.leash
?: error("Expected dragged leash to be non-null")
t.setRelativeLayer(change.leash, draggedTaskLeash, -i)
startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i)
}
}
- val draggedTaskChange = state.draggedTaskChange
+ val draggedTaskChange =
+ state.draggedTaskChange
?: throw IllegalStateException("Expected non-null change of dragged task")
val draggedTaskLeash = draggedTaskChange.leash
val startBounds = draggedTaskChange.startAbsBounds
@@ -395,57 +412,59 @@
val startPosition = state.dragAnimator.position
val unscaledStartWidth = startBounds.width()
val unscaledStartHeight = startBounds.height()
- val unscaledStartBounds = Rect(
- startPosition.x.toInt(),
- startPosition.y.toInt(),
- startPosition.x.toInt() + unscaledStartWidth,
- startPosition.y.toInt() + unscaledStartHeight
- )
+ val unscaledStartBounds =
+ Rect(
+ startPosition.x.toInt(),
+ startPosition.y.toInt(),
+ startPosition.x.toInt() + unscaledStartWidth,
+ startPosition.y.toInt() + unscaledStartHeight
+ )
dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t)
// Accept the merge by applying the merging transaction (applied by #showResizeVeil)
// and finish callback. Show the veil and position the task at the first frame before
// starting the final animation.
- onTaskResizeAnimationListener.onAnimationStart(state.draggedTaskId, t,
- unscaledStartBounds)
+ onTaskResizeAnimationListener.onAnimationStart(
+ state.draggedTaskId,
+ t,
+ unscaledStartBounds
+ )
finishCallback.onTransitionFinished(null /* wct */)
val tx: SurfaceControl.Transaction = transactionSupplier.get()
ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
- .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
- .apply {
- addUpdateListener { animator ->
- val animBounds = animator.animatedValue as Rect
- val animFraction = animator.animatedFraction
- // Progress scale from starting value to 1 as animation plays.
- val animScale = startScale + animFraction * (1 - startScale)
- tx.apply {
- setScale(draggedTaskLeash, animScale, animScale)
- setPosition(
- draggedTaskLeash,
- animBounds.left.toFloat(),
- animBounds.top.toFloat()
- )
- setWindowCrop(
- draggedTaskLeash,
- animBounds.width(),
- animBounds.height()
- )
- }
- onTaskResizeAnimationListener.onBoundsChange(
- state.draggedTaskId,
- tx,
- animBounds
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val animBounds = animator.animatedValue as Rect
+ val animFraction = animator.animatedFraction
+ // Progress scale from starting value to 1 as animation plays.
+ val animScale = startScale + animFraction * (1 - startScale)
+ tx.apply {
+ setScale(draggedTaskLeash, animScale, animScale)
+ setPosition(
+ draggedTaskLeash,
+ animBounds.left.toFloat(),
+ animBounds.top.toFloat()
)
+ setWindowCrop(draggedTaskLeash, animBounds.width(), animBounds.height())
}
- addListener(object : AnimatorListenerAdapter() {
+ onTaskResizeAnimationListener.onBoundsChange(
+ state.draggedTaskId,
+ tx,
+ animBounds
+ )
+ }
+ addListener(
+ object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
startTransitionFinishCb.onTransitionFinished(null /* null */)
clearState()
}
- })
- start()
- }
+ }
+ )
+ start()
+ }
} else if (isCancelTransition) {
info.changes.forEach { change ->
t.show(change.leash)
@@ -459,8 +478,8 @@
}
override fun handleRequest(
- transition: IBinder,
- request: TransitionRequestInfo
+ transition: IBinder,
+ request: TransitionRequestInfo
): WindowContainerTransaction? {
// Only handle transitions started from shell.
return null
@@ -489,8 +508,8 @@
val state = requireTransitionState()
val dragToDesktopAnimator = state.dragAnimator
- val draggedTaskChange = state.draggedTaskChange
- ?: throw IllegalStateException("Expected non-null task change")
+ val draggedTaskChange =
+ state.draggedTaskChange ?: throw IllegalStateException("Expected non-null task change")
val sc = draggedTaskChange.leash
// Pause the animation that shrinks the window when task is first dragged from fullscreen
dragToDesktopAnimator.cancelAnimator()
@@ -503,29 +522,31 @@
val dy = targetY - y
val tx: SurfaceControl.Transaction = transactionSupplier.get()
ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
- .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
- .apply {
- addUpdateListener { animator ->
- val scale = animator.animatedValue as Float
- val fraction = animator.animatedFraction
- val animX = x + (dx * fraction)
- val animY = y + (dy * fraction)
- tx.apply {
- setPosition(sc, animX, animY)
- setScale(sc, scale, scale)
- show(sc)
- apply()
- }
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val scale = animator.animatedValue as Float
+ val fraction = animator.animatedFraction
+ val animX = x + (dx * fraction)
+ val animY = y + (dy * fraction)
+ tx.apply {
+ setPosition(sc, animX, animY)
+ setScale(sc, scale, scale)
+ show(sc)
+ apply()
}
- addListener(object : AnimatorListenerAdapter() {
+ }
+ addListener(
+ object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
dragToDesktopStateListener?.onCancelToDesktopAnimationEnd(tx)
// Start the cancel transition to restore order.
startCancelDragToDesktopTransition()
}
- })
- start()
- }
+ }
+ )
+ start()
+ }
}
private fun startCancelDragToDesktopTransition() {
@@ -536,19 +557,23 @@
// There may have been tasks sent behind home that are not the dragged task (like
// when the dragged task is translucent and that makes the task behind it visible).
// Restore the order of those first.
- state.otherRootChanges.mapNotNull { it.container }.forEach { wc ->
- // TODO(b/322852244): investigate why even though these "other" tasks are
- // reordered in front of home and behind the translucent dragged task, its
- // surface is not visible on screen.
- wct.reorder(wc, true /* toTop */)
- }
- val wc = state.draggedTaskChange?.container
+ state.otherRootChanges
+ .mapNotNull { it.container }
+ .forEach { wc ->
+ // TODO(b/322852244): investigate why even though these "other" tasks are
+ // reordered in front of home and behind the translucent dragged task, its
+ // surface is not visible on screen.
+ wct.reorder(wc, true /* toTop */)
+ }
+ val wc =
+ state.draggedTaskChange?.container
?: error("Dragged task should be non-null before cancelling")
// Then the dragged task a the very top.
wct.reorder(wc, true /* toTop */)
}
is TransitionState.FromSplit -> {
- val wc = state.splitRootChange?.container
+ val wc =
+ state.splitRootChange?.container
?: error("Split root should be non-null before cancelling")
wct.reorder(wc, true /* toTop */)
}
@@ -556,8 +581,8 @@
val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
wct.restoreTransientOrder(homeWc)
- state.cancelTransitionToken = transitions.startTransition(
- TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
+ state.cancelTransitionToken =
+ transitions.startTransition(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
}
private fun clearState() {
@@ -571,11 +596,12 @@
private fun getOtherSplitTask(taskId: Int): Int? {
val splitPos = splitScreenController.getSplitPosition(taskId)
if (splitPos == SPLIT_POSITION_UNDEFINED) return null
- val otherTaskPos = if (splitPos == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- SPLIT_POSITION_TOP_OR_LEFT
- } else {
- SPLIT_POSITION_BOTTOM_OR_RIGHT
- }
+ val otherTaskPos =
+ if (splitPos == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ SPLIT_POSITION_TOP_OR_LEFT
+ } else {
+ SPLIT_POSITION_BOTTOM_OR_RIGHT
+ }
return splitScreenController.getTaskInfo(otherTaskPos)?.taskId
}
@@ -585,6 +611,7 @@
interface DragToDesktopStateListener {
fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction)
+
fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction)
}
@@ -601,31 +628,32 @@
abstract var startAborted: Boolean
data class FromFullscreen(
- override val draggedTaskId: Int,
- override val dragAnimator: MoveToDesktopAnimator,
- override val startTransitionToken: IBinder,
- override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
- override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
- override var cancelTransitionToken: IBinder? = null,
- override var homeToken: WindowContainerToken? = null,
- override var draggedTaskChange: Change? = null,
- override var cancelled: Boolean = false,
- override var startAborted: Boolean = false,
- var otherRootChanges: MutableList<Change> = mutableListOf()
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ override var startAborted: Boolean = false,
+ var otherRootChanges: MutableList<Change> = mutableListOf()
) : TransitionState()
+
data class FromSplit(
- override val draggedTaskId: Int,
- override val dragAnimator: MoveToDesktopAnimator,
- override val startTransitionToken: IBinder,
- override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
- override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
- override var cancelTransitionToken: IBinder? = null,
- override var homeToken: WindowContainerToken? = null,
- override var draggedTaskChange: Change? = null,
- override var cancelled: Boolean = false,
- override var startAborted: Boolean = false,
- var splitRootChange: Change? = null,
- var otherSplitTask: Int
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ override var startAborted: Boolean = false,
+ var splitRootChange: Change? = null,
+ var otherSplitTask: Int
) : TransitionState()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 74b8f83..526cf4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -59,6 +59,7 @@
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
private OnTaskResizeAnimationListener mOnTaskResizeAnimationListener;
+
public EnterDesktopTaskTransitionHandler(
Transitions transitions) {
this(transitions, SurfaceControl.Transaction::new);
@@ -72,11 +73,12 @@
}
void setOnTaskResizeAnimationListener(OnTaskResizeAnimationListener listener) {
- mOnTaskResizeAnimationListener = listener;
+ mOnTaskResizeAnimationListener = listener;
}
/**
* Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
+ *
* @param wct WindowContainerTransaction for transition
* @return the token representing the started transition
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index 7342bd1..9f9e256 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -56,7 +56,7 @@
private final Transitions mTransitions;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
- private Supplier<SurfaceControl.Transaction> mTransactionSupplier;
+ private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private Point mPosition;
public ExitDesktopTaskTransitionHandler(
@@ -76,9 +76,10 @@
/**
* Starts Transition of a given type
- * @param type Transition type
- * @param wct WindowContainerTransaction for transition
- * @param position Position of the task when transition is started
+ *
+ * @param type Transition type
+ * @param wct WindowContainerTransaction for transition
+ * @param position Position of the task when transition is started
* @param onAnimationEndCallback to be called after animation
*/
public void startTransition(@WindowManager.TransitionType int type,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index c469e65..88d0554 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -86,9 +86,9 @@
.setWindowCrop(leash, startBounds.width(), startBounds.height())
.show(leash)
onTaskResizeAnimationListener.onAnimationStart(
- taskId,
- startTransaction,
- startBounds
+ taskId,
+ startTransaction,
+ startBounds
)
},
onEnd = {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index e0e2e706..7d2aa27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -98,7 +98,7 @@
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
mDesktopModeTaskRepository.ifPresent(repository -> {
- repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
+ repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
if (taskInfo.isVisible) {
if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
@@ -120,7 +120,7 @@
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
mDesktopModeTaskRepository.ifPresent(repository -> {
- repository.removeFreeformTask(taskInfo.taskId);
+ repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
if (repository.removeActiveTask(taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
@@ -167,7 +167,7 @@
taskInfo.taskId, taskInfo.isFocused);
if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused) {
mDesktopModeTaskRepository.ifPresent(repository -> {
- repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
+ repository.addOrMoveFreeformTaskToTop(taskInfo.displayId, taskInfo.taskId);
repository.unminimizeTask(taskInfo.displayId, taskInfo.taskId);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index 6adbe4f..456658c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.transition.tracing;
-import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT;
+import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP;
import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellHandlerMapping;
import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellHandlerMappings;
@@ -52,7 +52,7 @@
DataSourceParams params =
new DataSourceParams.Builder()
.setBufferExhaustedPolicy(
- PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
.build();
mDataSource.register(params);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 9afb057..74b091f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -278,11 +278,9 @@
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
if (visible && stage != STAGE_TYPE_UNDEFINED) {
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
- if (decor == null || !DesktopModeStatus.canEnterDesktopMode(mContext)
- || decor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- return;
+ if (decor != null && DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ mDesktopTasksController.moveToSplit(decor.mTaskInfo);
}
- mDesktopTasksController.moveToSplit(decor.mTaskInfo);
}
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 53bdda1..78f0ef7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -28,7 +28,6 @@
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ProgressBar
-import androidx.annotation.ColorInt
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.content.ContextCompat
@@ -106,30 +105,17 @@
fun setAnimationTints(
darkMode: Boolean,
iconForegroundColor: ColorStateList? = null,
- baseForegroundColor: Int? = null
+ baseForegroundColor: Int? = null,
+ rippleDrawable: RippleDrawable? = null
) {
if (Flags.enableThemedAppHeaders()) {
requireNotNull(iconForegroundColor) { "Icon foreground color must be non-null" }
requireNotNull(baseForegroundColor) { "Base foreground color must be non-null" }
+ requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
maximizeWindow.imageTintList = iconForegroundColor
- maximizeWindow.background = RippleDrawable(
- ColorStateList(
- arrayOf(
- intArrayOf(android.R.attr.state_hovered),
- intArrayOf(android.R.attr.state_pressed),
- intArrayOf(),
- ),
- intArrayOf(
- replaceColorAlpha(baseForegroundColor, OPACITY_8),
- replaceColorAlpha(baseForegroundColor, OPACITY_12),
- Color.TRANSPARENT
- )
- ),
- null,
- null
- )
+ maximizeWindow.background = rippleDrawable
progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
- .withAlpha(OPACITY_12)
+ .withAlpha(OPACITY_15)
progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
} else {
if (darkMode) {
@@ -146,18 +132,7 @@
}
}
- @ColorInt
- private fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int {
- return Color.argb(
- alpha,
- Color.red(color),
- Color.green(color),
- Color.blue(color)
- )
- }
-
companion object {
- private const val OPACITY_8 = 20
- private const val OPACITY_12 = 31
+ private const val OPACITY_15 = 38
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 1c4b742..3c12da2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -9,6 +9,9 @@
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RippleDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RoundRectShape
import android.view.View
import android.view.View.OnLongClickListener
import android.widget.ImageButton
@@ -46,6 +49,38 @@
onMaximizeHoverAnimationFinishedListener: () -> Unit
) : DesktopModeWindowDecorationViewHolder(rootView) {
+ /**
+ * The corner radius to apply to the app chip, maximize and close button's background drawable.
+ **/
+ private val headerButtonsRippleRadius = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_buttons_ripple_radius)
+
+ /**
+ * The app chip, maximize and close button's height extends to the top & bottom edges of the
+ * header, and their width may be larger than their height. This is by design to increase the
+ * clickable and hover-able bounds of the view as much as possible. However, to prevent the
+ * ripple drawable from being as large as the views (and asymmetrical), insets are applied to
+ * the background ripple drawable itself to give the appearance of a smaller button
+ * (with padding between itself and the header edges / sibling buttons) but without affecting
+ * its touchable region.
+ */
+ private val appChipDrawableInsets = DrawableInsets(
+ vertical = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_app_chip_ripple_inset_vertical)
+ )
+ private val maximizeDrawableInsets = DrawableInsets(
+ vertical = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_vertical),
+ horizontal = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_horizontal)
+ )
+ private val closeDrawableInsets = DrawableInsets(
+ vertical = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_close_ripple_inset_vertical),
+ horizontal = context.resources
+ .getDimensionPixelSize(R.dimen.desktop_mode_header_close_ripple_inset_horizontal)
+ )
+
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: View = rootView.requireViewById(R.id.caption_handle)
private val openMenuButton: View = rootView.requireViewById(R.id.open_menu_button)
@@ -97,7 +132,19 @@
maximizeWindowButton.imageAlpha = alpha
closeWindowButton.imageAlpha = alpha
expandMenuButton.imageAlpha = alpha
-
+ context.withStyledAttributes(
+ set = null,
+ attrs = intArrayOf(
+ android.R.attr.selectableItemBackground,
+ android.R.attr.selectableItemBackgroundBorderless
+ ),
+ defStyleAttr = 0,
+ defStyleRes = 0
+ ) {
+ openMenuButton.background = getDrawable(0)
+ maximizeWindowButton.background = getDrawable(1)
+ closeWindowButton.background = getDrawable(1)
+ }
maximizeButtonView.setAnimationTints(isDarkMode())
}
@@ -126,18 +173,40 @@
val foregroundColor = headerStyle.foreground.color
val foregroundAlpha = headerStyle.foreground.opacity
val colorStateList = ColorStateList.valueOf(foregroundColor).withAlpha(foregroundAlpha)
- closeWindowButton.imageTintList = colorStateList
- expandMenuButton.imageTintList = colorStateList
- with (appNameTextView) {
- isVisible = header.type == Header.Type.DEFAULT
- setTextColor(colorStateList)
+ // App chip.
+ openMenuButton.apply {
+ background = createRippleDrawable(
+ color = foregroundColor,
+ cornerRadius = headerButtonsRippleRadius,
+ drawableInsets = appChipDrawableInsets,
+ )
+ expandMenuButton.imageTintList = colorStateList
+ appNameTextView.apply {
+ isVisible = header.type == Header.Type.DEFAULT
+ setTextColor(colorStateList)
+ }
+ appIconImageView.imageAlpha = foregroundAlpha
}
- appIconImageView.imageAlpha = foregroundAlpha
+ // Maximize button.
maximizeButtonView.setAnimationTints(
darkMode = header.appTheme == Header.Theme.DARK,
iconForegroundColor = colorStateList,
- baseForegroundColor = foregroundColor
+ baseForegroundColor = foregroundColor,
+ rippleDrawable = createRippleDrawable(
+ color = foregroundColor,
+ cornerRadius = headerButtonsRippleRadius,
+ drawableInsets = maximizeDrawableInsets
+ )
)
+ // Close button.
+ closeWindowButton.apply {
+ imageTintList = colorStateList
+ background = createRippleDrawable(
+ color = foregroundColor,
+ cornerRadius = headerButtonsRippleRadius,
+ drawableInsets = closeDrawableInsets
+ )
+ }
}
override fun onHandleMenuOpened() {}
@@ -390,10 +459,61 @@
context.withStyledAttributes(null, intArrayOf(attr), 0, 0) {
return getColor(0, 0)
}
- return Color.BLACK
+ return Color.WHITE
}
- data class Header(
+ @ColorInt
+ private fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int {
+ return Color.argb(
+ alpha,
+ Color.red(color),
+ Color.green(color),
+ Color.blue(color)
+ )
+ }
+
+ private fun createRippleDrawable(
+ @ColorInt color: Int,
+ cornerRadius: Int,
+ drawableInsets: DrawableInsets,
+ ): RippleDrawable {
+ return RippleDrawable(
+ ColorStateList(
+ arrayOf(
+ intArrayOf(android.R.attr.state_hovered),
+ intArrayOf(android.R.attr.state_pressed),
+ intArrayOf(),
+ ),
+ intArrayOf(
+ replaceColorAlpha(color, OPACITY_11),
+ replaceColorAlpha(color, OPACITY_15),
+ Color.TRANSPARENT
+ )
+ ),
+ null /* content */,
+ LayerDrawable(arrayOf(
+ ShapeDrawable().apply {
+ shape = RoundRectShape(
+ FloatArray(8) { cornerRadius.toFloat() },
+ null /* inset */,
+ null /* innerRadii */
+ )
+ paint.color = Color.WHITE
+ }
+ )).apply {
+ require(numberOfLayers == 1) { "Must only contain one layer" }
+ setLayerInset(0 /* index */,
+ drawableInsets.l, drawableInsets.t, drawableInsets.r, drawableInsets.b)
+ }
+ )
+ }
+
+ private data class DrawableInsets(val l: Int, val t: Int, val r: Int, val b: Int) {
+ constructor(vertical: Int = 0, horizontal: Int = 0) :
+ this(horizontal, vertical, horizontal, vertical)
+ }
+
+ private data class Header(
val type: Type,
val systemTheme: Theme,
val appTheme: Theme,
@@ -408,7 +528,7 @@
private fun Header.Theme.isDark(): Boolean = this == Header.Theme.DARK
- data class HeaderStyle(
+ private data class HeaderStyle(
val background: Background,
val foreground: Foreground
) {
@@ -497,6 +617,8 @@
private const val FOCUSED_OPACITY = 255
private const val OPACITY_100 = 255
+ private const val OPACITY_11 = 28
+ private const val OPACITY_15 = 38
private const val OPACITY_30 = 77
private const val OPACITY_55 = 140
private const val OPACITY_65 = 166
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index f2f10ae..d03d779 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -188,6 +188,13 @@
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ /** {@inheritDoc} */
+ @Test
+ @FlakyTest(bugId = 336510055)
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 8f59f30..310ccc2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -363,11 +363,11 @@
@Test
fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() {
- repo.addOrMoveFreeformTaskToTop(5)
- repo.addOrMoveFreeformTaskToTop(6)
- repo.addOrMoveFreeformTaskToTop(7)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
- val tasks = repo.getFreeformTasksInZOrder()
+ val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
assertThat(tasks.size).isEqualTo(3)
assertThat(tasks[0]).isEqualTo(7)
assertThat(tasks[1]).isEqualTo(6)
@@ -376,13 +376,13 @@
@Test
fun addOrMoveFreeformTaskToTop_alreadyExists_movesToTop() {
- repo.addOrMoveFreeformTaskToTop(5)
- repo.addOrMoveFreeformTaskToTop(6)
- repo.addOrMoveFreeformTaskToTop(7)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
- repo.addOrMoveFreeformTaskToTop(6)
+ repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
- val tasks = repo.getFreeformTasksInZOrder()
+ val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
assertThat(tasks.size).isEqualTo(3)
assertThat(tasks.first()).isEqualTo(6)
}
@@ -391,7 +391,7 @@
fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
val taskId = 1
repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
- repo.removeFreeformTask(taskId)
+ repo.removeFreeformTask(THIRD_DISPLAY, taskId)
assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
}
@@ -480,31 +480,31 @@
@Test
fun getActiveNonMinimizedTasksOrderedFrontToBack_returnsFreeformTasksInCorrectOrder() {
- repo.addActiveTask(displayId = 0, taskId = 1)
- repo.addActiveTask(displayId = 0, taskId = 2)
- repo.addActiveTask(displayId = 0, taskId = 3)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
// The front-most task will be the one added last through addOrMoveFreeformTaskToTop
- repo.addOrMoveFreeformTaskToTop(taskId = 3)
- repo.addOrMoveFreeformTaskToTop(taskId = 2)
- repo.addOrMoveFreeformTaskToTop(taskId = 1)
+ repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
+ repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 2)
+ repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 1)
- assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0)).isEqualTo(
- listOf(1, 2, 3))
+ assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0))
+ .containsExactly(1, 2, 3).inOrder()
}
@Test
fun getActiveNonMinimizedTasksOrderedFrontToBack_minimizedTaskNotIncluded() {
- repo.addActiveTask(displayId = 0, taskId = 1)
- repo.addActiveTask(displayId = 0, taskId = 2)
- repo.addActiveTask(displayId = 0, taskId = 3)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+ repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3)
// The front-most task will be the one added last through addOrMoveFreeformTaskToTop
- repo.addOrMoveFreeformTaskToTop(taskId = 3)
- repo.addOrMoveFreeformTaskToTop(taskId = 2)
- repo.addOrMoveFreeformTaskToTop(taskId = 1)
- repo.minimizeTask(displayId = 0, taskId = 2)
+ repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 3)
+ repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 2)
+ repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1)
+ repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
- assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(displayId = 0)).isEqualTo(
- listOf(1, 3))
+ assertThat(repo.getActiveNonMinimizedTasksOrderedFrontToBack(
+ displayId = DEFAULT_DISPLAY)).containsExactly(1, 3).inOrder()
}
@@ -544,5 +544,6 @@
companion object {
const val SECOND_DISPLAY = 1
+ const val THIRD_DISPLAY = 345
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index d8d534b..ac67bd1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -1614,7 +1614,7 @@
val task = createFreeformTask(displayId, bounds)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
- desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId)
+ desktopModeTaskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
runningTasks.add(task)
return task
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 3c488ca..77f917c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -298,7 +298,7 @@
val task = createFreeformTask(displayId)
`when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
desktopTaskRepo.addActiveTask(displayId, task.taskId)
- desktopTaskRepo.addOrMoveFreeformTaskToTop(task.taskId)
+ desktopTaskRepo.addOrMoveFreeformTaskToTop(displayId, task.taskId)
return task
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index cd68c69..3f3cafc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -91,7 +91,8 @@
mFreeformTaskListener.onFocusTaskChanged(task);
- verify(mDesktopModeTaskRepository).addOrMoveFreeformTaskToTop(task.taskId);
+ verify(mDesktopModeTaskRepository)
+ .addOrMoveFreeformTaskToTop(task.displayId, task.taskId);
}
@Test
@@ -103,7 +104,7 @@
mFreeformTaskListener.onFocusTaskChanged(fullscreenTask);
verify(mDesktopModeTaskRepository, never())
- .addOrMoveFreeformTaskToTop(fullscreenTask.taskId);
+ .addOrMoveFreeformTaskToTop(fullscreenTask.displayId, fullscreenTask.taskId);
}
@After
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 63b4538..386a606c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8617,6 +8617,7 @@
@SystemApi
@NonNull
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ // TODO also open to MODIFY_AUDIO_SETTINGS_PRIVILEGED b/341780042
public static List<AudioVolumeGroup> getAudioVolumeGroups() {
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e612645..c8b9da5 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -142,7 +142,7 @@
@UnsupportedAppUsage
int getStreamMaxVolume(int streamType);
- @EnforcePermission("MODIFY_AUDIO_ROUTING")
+ @EnforcePermission(anyOf={"MODIFY_AUDIO_SETTINGS_PRIVILEGED", "MODIFY_AUDIO_ROUTING"})
List<AudioVolumeGroup> getAudioVolumeGroups();
@EnforcePermission(anyOf={"MODIFY_AUDIO_SETTINGS_PRIVILEGED", "MODIFY_AUDIO_ROUTING"})
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index d607126..0f5fbcc 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* A class to create the association between different playback attributes
@@ -118,6 +119,12 @@
&& Arrays.equals(mAudioAttributes, thatAvg.mAudioAttributes);
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mId,
+ Arrays.hashCode(mAudioAttributes), Arrays.hashCode(mLegacyStreamTypes));
+ }
+
/**
* @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
index 5bcb82d..0b71d25 100644
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
@@ -27,6 +27,7 @@
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -62,16 +63,18 @@
captioningChanges
.filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class)
.map { it.isEnabled }
+ .onStart { emit(captioningManager.isSystemAudioCaptioningEnabled) }
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
- captioningManager.isSystemAudioCaptioningEnabled
+ captioningManager.isSystemAudioCaptioningEnabled,
)
override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> =
captioningChanges
.filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class)
.map { it.isEnabled }
+ .onStart { emit(captioningManager.isSystemAudioCaptioningUiEnabled) }
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 3b84333..8204569 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -16,9 +16,13 @@
package com.android.settingslib.volume.data.repository
+import android.content.ContentResolver
+import android.database.ContentObserver
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.OnCommunicationDeviceChangedListener
+import android.provider.Settings
+import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.settingslib.volume.shared.model.AudioManagerEvent
@@ -33,12 +37,13 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -84,17 +89,31 @@
class AudioRepositoryImpl(
private val audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val audioManager: AudioManager,
+ private val contentResolver: ContentResolver,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
) : AudioRepository {
+ private val streamSettingNames: Map<AudioStream, String> =
+ mapOf(
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to Settings.System.VOLUME_VOICE,
+ AudioStream(AudioManager.STREAM_SYSTEM) to Settings.System.VOLUME_SYSTEM,
+ AudioStream(AudioManager.STREAM_RING) to Settings.System.VOLUME_RING,
+ AudioStream(AudioManager.STREAM_MUSIC) to Settings.System.VOLUME_MUSIC,
+ AudioStream(AudioManager.STREAM_ALARM) to Settings.System.VOLUME_ALARM,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to Settings.System.VOLUME_NOTIFICATION,
+ AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to Settings.System.VOLUME_BLUETOOTH_SCO,
+ AudioStream(AudioManager.STREAM_ACCESSIBILITY) to Settings.System.VOLUME_ACCESSIBILITY,
+ AudioStream(AudioManager.STREAM_ASSISTANT) to Settings.System.VOLUME_ASSISTANT,
+ )
+
override val mode: StateFlow<Int> =
callbackFlow {
- val listener =
- AudioManager.OnModeChangedListener { newMode -> launch { send(newMode) } }
+ val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) }
audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener)
awaitClose { audioManager.removeOnModeChangedListener(listener) }
}
+ .onStart { emit(audioManager.mode) }
.flowOn(backgroundCoroutineContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
@@ -102,6 +121,7 @@
audioManagerEventsReceiver.events
.filterIsInstance(AudioManagerEvent.InternalRingerModeChanged::class)
.map { RingerMode(audioManager.ringerModeInternal) }
+ .onStart { emit(RingerMode(audioManager.ringerModeInternal)) }
.flowOn(backgroundCoroutineContext)
.stateIn(
coroutineScope,
@@ -122,6 +142,7 @@
}
.filterNotNull()
.map { audioManager.communicationDevice }
+ .onStart { emit(audioManager.communicationDevice) }
.flowOn(backgroundCoroutineContext)
.stateIn(
coroutineScope,
@@ -130,17 +151,18 @@
)
override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
- return audioManagerEventsReceiver.events
- .filter {
- if (it is StreamAudioManagerEvent) {
- it.audioStream == audioStream
- } else {
- true
- }
- }
+ return merge(
+ audioManagerEventsReceiver.events.filter {
+ if (it is StreamAudioManagerEvent) {
+ it.audioStream == audioStream
+ } else {
+ true
+ }
+ },
+ volumeSettingChanges(audioStream),
+ )
.map { getCurrentAudioStream(audioStream) }
.onStart { emit(getCurrentAudioStream(audioStream)) }
- .conflate()
.flowOn(backgroundCoroutineContext)
}
@@ -195,4 +217,19 @@
// return STREAM_VOICE_CALL in getAudioStream
audioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL)
}
+
+ private fun volumeSettingChanges(audioStream: AudioStream): Flow<Unit> {
+ val uri = streamSettingNames[audioStream]?.let(Settings.System::getUriFor)
+ uri ?: return emptyFlow()
+ return callbackFlow {
+ val observer =
+ object : ContentObserver(DirectExecutor.INSTANCE, 0) {
+ override fun onChange(selfChange: Boolean) {
+ launch { send(Unit) }
+ }
+ }
+ contentResolver.registerContentObserver(uri, false, observer)
+ awaitClose { contentResolver.unregisterContentObserver(observer) }
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 8700680..683759d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -16,6 +16,8 @@
package com.android.settingslib.volume.data.repository
+import android.content.ContentResolver
+import android.database.ContentObserver
import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,6 +40,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
@@ -55,9 +58,11 @@
@Captor
private lateinit var communicationDeviceListenerCaptor:
ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener>
+ @Captor private lateinit var contentObserver: ArgumentCaptor<ContentObserver>
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
+ @Mock private lateinit var contentResolver: ContentResolver
private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
@@ -80,6 +85,7 @@
val streamType = it.arguments[0] as Int
volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
triggerEvent(AudioManagerEvent.StreamVolumeChanged(AudioStream(streamType)))
+ triggerSettingChange()
}
`when`(audioManager.adjustStreamVolume(anyInt(), anyInt(), anyInt())).then {
val streamType = it.arguments[0] as Int
@@ -100,6 +106,7 @@
AudioRepositoryImpl(
eventsReceiver,
audioManager,
+ contentResolver,
testScope.testScheduler,
testScope.backgroundScope,
)
@@ -254,6 +261,12 @@
modeListenerCaptor.value.onModeChanged(mode)
}
+ private fun triggerSettingChange(selfChange: Boolean = false) {
+ verify(contentResolver)
+ .registerContentObserver(any(), anyBoolean(), contentObserver.capture())
+ contentObserver.value.onChange(selfChange)
+ }
+
private fun triggerEvent(event: AudioManagerEvent) {
testScope.launch { eventsReceiver.triggerEvent(event) }
}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index ee81813..0dd9275 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -36,6 +36,7 @@
import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.text.Html;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -253,6 +254,9 @@
} else {
p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
}
+ } else {
+ // Make sure intents don't inject HTML elements.
+ p.mTitle = Html.escapeHtml(p.mTitle.toString());
}
setupAlert();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ac680a9..e940674 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -752,6 +752,7 @@
plugins: [
"dagger2-compiler",
],
+ strict_mode: false,
}
// in-place tests which use Robolectric in the tests directory
@@ -771,7 +772,6 @@
],
static_libs: [
"RoboTestLibraries",
- "mockito-kotlin2",
],
libs: [
"android.test.runner",
@@ -787,6 +787,7 @@
plugins: [
"dagger2-compiler",
],
+ strict_mode: false,
}
android_ravenwood_test {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 163c849..780a099 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -964,6 +964,9 @@
namespace: "systemui"
description: "Only dismiss media notifications when the control was removed by the user."
bug: "335875159"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index f5d01d7..907c39d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -944,9 +944,26 @@
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- startController.onTransitionAnimationEnd(isExpandingFullyAbove)
- endController.onTransitionAnimationEnd(isExpandingFullyAbove)
- onLaunchAnimationEnd()
+ // onLaunchAnimationEnd is called by an Animator at the end of the animation,
+ // on a Choreographer animation tick. The following calls will move the animated
+ // content from the dialog overlay back to its original position, and this
+ // change must be reflected in the next frame given that we then sync the next
+ // frame of both the content and dialog ViewRoots. However, in case that content
+ // is rendered by Compose, whose compositions are also scheduled on a
+ // Choreographer frame, any state change made *right now* won't be reflected in
+ // the next frame given that a Choreographer frame can't schedule another and
+ // have it happen in the same frame. So we post the forwarded calls to
+ // [Controller.onLaunchAnimationEnd], leaving this Choreographer frame, ensuring
+ // that the move of the content back to its original window will be reflected in
+ // the next frame right after [onLaunchAnimationEnd] is called.
+ //
+ // TODO(b/330672236): Move this to TransitionAnimator.
+ dialog.context.mainExecutor.execute {
+ startController.onTransitionAnimationEnd(isExpandingFullyAbove)
+ endController.onTransitionAnimationEnd(isExpandingFullyAbove)
+
+ onLaunchAnimationEnd()
+ }
}
override fun onTransitionAnimationProgress(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index cc55df1..8e824e6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -357,26 +357,13 @@
Log.d(TAG, "Animation ended")
}
- // onAnimationEnd is called at the end of the animation, on a Choreographer
- // animation tick. During dialog launches, the following calls will move the
- // animated content from the dialog overlay back to its original position, and
- // this change must be reflected in the next frame given that we then sync the
- // next frame of both the content and dialog ViewRoots. During SysUI activity
- // launches, we will instantly collapse the shade at the end of the transition.
- // However, if those are rendered by Compose, whose compositions are also
- // scheduled on a Choreographer frame, any state change made *right now* won't
- // be reflected in the next frame given that a Choreographer frame can't
- // schedule another and have it happen in the same frame. So we post the
- // forwarded calls to [Controller.onLaunchAnimationEnd] in the main executor,
- // leaving this Choreographer frame, ensuring that any state change applied by
- // onTransitionAnimationEnd() will be reflected in the same frame.
- mainExecutor.execute {
- controller.onTransitionAnimationEnd(isExpandingFullyAbove)
- transitionContainerOverlay.remove(windowBackgroundLayer)
+ // TODO(b/330672236): Post this to the main thread instead so that it does not
+ // flicker with Flexiglass enabled.
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+ transitionContainerOverlay.remove(windowBackgroundLayer)
- if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
- openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
- }
+ if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+ openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 99f7d0f..887e349 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -28,7 +28,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import javax.inject.Inject
/**
* Renders the content of the lockscreen.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index cf2e895..e6132c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -169,6 +169,7 @@
viewModel: NotificationsPlaceholderViewModel,
maxScrimTop: () -> Float,
shouldPunchHoleBehindScrim: Boolean,
+ shouldFillMaxSize: Boolean = true,
shadeMode: ShadeMode,
modifier: Modifier = Modifier,
) {
@@ -327,14 +328,14 @@
}
Box(
modifier =
- Modifier.fillMaxSize()
- .graphicsLayer {
+ Modifier.graphicsLayer {
alpha =
if (shouldPunchHoleBehindScrim) {
(expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f)
} else 1f
}
.background(MaterialTheme.colorScheme.surface)
+ .thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() }
.debugBackground(viewModel, DEBUG_BOX_COLOR)
) {
NotificationPlaceholder(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index e17a146..ae53d56 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -17,21 +17,31 @@
package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
+import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -41,36 +51,53 @@
class NotificationsShadeScene
@Inject
constructor(
- viewModel: NotificationsShadeSceneViewModel,
+ sceneViewModel: NotificationsShadeSceneViewModel,
private val overlayShadeViewModel: OverlayShadeViewModel,
+ private val shadeHeaderViewModel: ShadeHeaderViewModel,
+ private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+ private val tintedIconManagerFactory: TintedIconManager.Factory,
+ private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+ private val statusBarIconController: StatusBarIconController,
+ private val shadeSession: SaveableSession,
+ private val stackScrollView: Lazy<NotificationScrollView>,
private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
) : ComposableScene {
override val key = Scenes.NotificationsShade
override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
- viewModel.destinationScenes
+ sceneViewModel.destinationScenes
@Composable
override fun SceneScope.Content(
modifier: Modifier,
) {
OverlayShade(
- viewModel = overlayShadeViewModel,
modifier = modifier,
+ viewModel = overlayShadeViewModel,
horizontalArrangement = Arrangement.Start,
lockscreenContent = lockscreenContent,
) {
- Text(
- text = "Notifications list",
- modifier = Modifier.padding(NotificationsShade.Dimensions.Padding)
- )
- }
- }
-}
+ Column {
+ ExpandedShadeHeader(
+ viewModel = shadeHeaderViewModel,
+ createTintedIconManager = tintedIconManagerFactory::create,
+ createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+ statusBarIconController = statusBarIconController,
+ modifier = Modifier.padding(horizontal = 16.dp),
+ )
-object NotificationsShade {
- object Dimensions {
- val Padding = 16.dp
+ NotificationScrollingStack(
+ shadeSession = shadeSession,
+ stackScrollView = stackScrollView.get(),
+ viewModel = notificationsPlaceholderViewModel,
+ maxScrimTop = { 0f },
+ shouldPunchHoleBehindScrim = false,
+ shouldFillMaxSize = false,
+ shadeMode = ShadeMode.Dual,
+ modifier = Modifier.width(416.dp),
+ )
+ }
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 04f76f5..4d946bf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -33,9 +33,9 @@
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import dagger.Lazy
+import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
-import java.util.Optional
@SysUISingleton
class QuickSettingsShadeScene
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index cbaa894..f5a0ef2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -8,12 +8,16 @@
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.goneToNotificationsShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.goneToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.goneToSplitShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToBouncerTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToCommunalTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToNotificationsShadeTransition
+import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToQuickSettingsTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition
@@ -37,6 +41,7 @@
// Scene transitions
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
+ from(Scenes.Gone, to = Scenes.NotificationsShade) { goneToNotificationsShadeTransition() }
from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
from(
Scenes.Gone,
@@ -53,8 +58,15 @@
goneToShadeTransition(durationScale = 0.9)
}
from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
+ from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
+ from(Scenes.Lockscreen, to = Scenes.NotificationsShade) {
+ lockscreenToNotificationsShadeTransition()
+ }
+ from(Scenes.Lockscreen, to = Scenes.QuickSettingsShade) {
+ lockscreenToQuickSettingsShadeTransition()
+ }
from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() }
from(
Scenes.Lockscreen,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
new file mode 100644
index 0000000..48ec198
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.goneToNotificationsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ toNotificationsShadeTransition(durationScale)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt
new file mode 100644
index 0000000..225ca4e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.goneToQuickSettingsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ toQuickSettingsShadeTransition(durationScale)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt
new file mode 100644
index 0000000..372e4a1a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.lockscreenToNotificationsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ toNotificationsShadeTransition(durationScale)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt
new file mode 100644
index 0000000..ce24f5e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import com.android.compose.animation.scene.TransitionBuilder
+
+fun TransitionBuilder.lockscreenToQuickSettingsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ toQuickSettingsShadeTransition(durationScale)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
new file mode 100644
index 0000000..a6b268d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
+import com.android.systemui.notifications.ui.composable.Notifications
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
+import kotlin.time.Duration.Companion.milliseconds
+
+fun TransitionBuilder.toNotificationsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+ swipeSpec =
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+ )
+ distance =
+ object : UserActionDistance {
+ override fun UserActionDistanceScope.absoluteDistance(
+ fromSceneSize: IntSize,
+ orientation: Orientation,
+ ): Float {
+ return fromSceneSize.height.toFloat() * 2 / 3f
+ }
+ }
+
+ translate(OverlayShade.Elements.PanelBackground, Edge.Top)
+ translate(Notifications.Elements.NotificationScrim, Edge.Top)
+
+ fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
+
+ fractionRange(start = .5f) {
+ fade(ShadeHeader.Elements.Clock)
+ fade(ShadeHeader.Elements.ExpandedContent)
+ fade(ShadeHeader.Elements.PrivacyChip)
+ fade(Notifications.Elements.NotificationScrim)
+ }
+}
+
+private val DefaultDuration = 300.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
new file mode 100644
index 0000000..2baaecf
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.composable.transitions
+
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.IntSize
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
+import com.android.compose.animation.scene.UserActionDistanceScope
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.shade.ui.composable.Shade
+import kotlin.time.Duration.Companion.milliseconds
+
+fun TransitionBuilder.toQuickSettingsShadeTransition(
+ durationScale: Double = 1.0,
+) {
+ spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
+ swipeSpec =
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
+ )
+ distance =
+ object : UserActionDistance {
+ override fun UserActionDistanceScope.absoluteDistance(
+ fromSceneSize: IntSize,
+ orientation: Orientation,
+ ): Float {
+ return fromSceneSize.height.toFloat() * 2 / 3f
+ }
+ }
+
+ translate(OverlayShade.Elements.PanelBackground, Edge.Top)
+
+ fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
+}
+
+private val DefaultDuration = 300.milliseconds
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 2f11085..34cc676 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
@@ -23,9 +23,12 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
@@ -37,6 +40,8 @@
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
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
@@ -54,6 +59,8 @@
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) {
@@ -66,10 +73,13 @@
Scrim(onClicked = viewModel::onScrimClicked)
Row(
- modifier = Modifier.fillMaxSize().padding(OverlayShade.Dimensions.ScrimContentPadding),
+ modifier =
+ Modifier.fillMaxSize().thenIf(!isPanelFullWidth) {
+ Modifier.padding(OverlayShade.Dimensions.ScrimContentPadding)
+ },
horizontalArrangement = horizontalArrangement,
) {
- Panel(content = content)
+ Panel(modifier = Modifier.panelSize(), content = content)
}
}
}
@@ -111,6 +121,20 @@
}
}
+@Composable
+private fun Modifier.panelSize(): Modifier {
+ val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
+
+ return this.then(
+ when (widthSizeClass) {
+ WindowWidthSizeClass.Compact -> Modifier.fillMaxWidth()
+ WindowWidthSizeClass.Medium -> Modifier.width(OverlayShade.Dimensions.PanelWidthMedium)
+ WindowWidthSizeClass.Expanded -> Modifier.width(OverlayShade.Dimensions.PanelWidthLarge)
+ else -> error("Unsupported WindowWidthSizeClass \"$widthSizeClass\"")
+ }
+ )
+}
+
object OverlayShade {
object Elements {
val Scrim = ElementKey("OverlayShadeScrim", scenePicker = LowestZIndexScenePicker)
@@ -127,6 +151,8 @@
object Dimensions {
val ScrimContentPadding = 16.dp
val PanelCornerRadius = 46.dp
+ val PanelWidthMedium = 390.dp
+ val PanelWidthLarge = 474.dp
}
object Shapes {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 83b8158..ab14911 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -23,7 +23,6 @@
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -43,16 +42,7 @@
fun VolumePanelRoot(
viewModel: VolumePanelViewModel,
modifier: Modifier = Modifier,
- onDismiss: () -> Unit
) {
- LaunchedEffect(viewModel) {
- viewModel.volumePanelState.collect {
- if (!it.isVisible) {
- onDismiss()
- }
- }
- }
-
val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md
index b2424f4..ade5171 100644
--- a/packages/SystemUI/docs/demo_mode.md
+++ b/packages/SystemUI/docs/demo_mode.md
@@ -22,42 +22,45 @@
<br/>
Commands are sent as string extras with key ```command``` (required). Possible values are:
-| Command | Subcommand | Argument | Description
-| --- | --- | --- | ---
-| ```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
-| ```exit``` | | | Exits demo mode, bars back to their system-driven state
-| ```battery``` | | | Control the battery display
-| | ```level``` | | Sets the battery level (0 - 100)
-| | ```plugged``` | | Sets charging state (```true```, ```false```)
-| | ```powersave``` | | Sets power save mode (```true```, ```anything else```)
-| ```network``` | | | Control the RSSI display
-| | ```airplane``` | | ```show``` to show icon, any other value to hide
-| | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
-| | ```wifi``` | | ```show``` to show icon, any other value to hide
-| | | ```level``` | Sets wifi level (null or 0-4)
-| | ```mobile``` | | ```show``` to show icon, any other value to hide
-| | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
-| | | ```level``` | Sets mobile signal strength level (null or 0-4)
-| | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide)
-| | ```sims``` | | Sets the number of sims (1-8)
-| | ```nosim``` | | ```show``` to show icon, any other value to hide
-| ```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
-| | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
-| ```status``` | | | Control the system status icons
-| | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
-| | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
-| | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
-| | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
-| | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
-| | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
-| | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
-| | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
-| | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
-| ```notifications``` | | | Control the notification icons
-| | ```visible``` | | ```false``` to hide the notification icons, any other value to show
-| ```clock``` | | | Control the clock display
-| | ```millis``` | | Sets the time in millis
-| | ```hhmm``` | | Sets the time in hh:mm
+| Command | Subcommand | Argument | Description
+| --- |----------------------------|------------------| ---
+| ```enter``` | | | Enters demo mode, bar state allowed to be modified (for convenience, any of the other non-exit commands will automatically flip demo mode on, no need to call this explicitly in practice)
+| ```exit``` | | | Exits demo mode, bars back to their system-driven state
+| ```battery``` | | | Control the battery display
+| | ```level``` | | Sets the battery level (0 - 100)
+| | ```plugged``` | | Sets charging state (```true```, ```false```)
+| | ```powersave``` | | Sets power save mode (```true```, ```anything else```)
+| ```network``` | | | Control the RSSI display
+| | ```airplane``` | | ```show``` to show icon, any other value to hide
+| | ```fully``` | | Sets MCS state to fully connected (```true```, ```false```)
+| | ```wifi``` | | ```show``` to show icon, any other value to hide
+| | | ```level``` | Sets wifi level (null or 0-4)
+| | ```mobile``` | | ```show``` to show icon, any other value to hide
+| | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide
+| | | ```level``` | Sets mobile signal strength level (null or 0-4)
+| | ```satellite``` | | ```show``` to show icon, any other value to hide
+| | | ```connection``` | ```connected```, ```off```, ```on```, or ```unknown``` (matches SatelliteConnectionState enum)
+| | | ```level``` | Sets satellite signal strength level (0-4)
+| | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide)
+| | ```sims``` | | Sets the number of sims (1-8)
+| | ```nosim``` | | ```show``` to show icon, any other value to hide
+| ```bars``` | | | Control the visual style of the bars (opaque, translucent, etc)
+| | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent)
+| ```status``` | | | Control the system status icons
+| | ```volume``` | | Sets the icon in the volume slot (```silent```, ```vibrate```, any other value to hide)
+| | ```bluetooth``` | | Sets the icon in the bluetooth slot (```connected```, ```disconnected```, any other value to hide)
+| | ```location``` | | Sets the icon in the location slot (```show```, any other value to hide)
+| | ```alarm``` | | Sets the icon in the alarm_clock slot (```show```, any other value to hide)
+| | ```sync``` | | Sets the icon in the sync_active slot (```show```, any other value to hide)
+| | ```tty``` | | Sets the icon in the tty slot (```show```, any other value to hide)
+| | ```eri``` | | Sets the icon in the cdma_eri slot (```show```, any other value to hide)
+| | ```mute``` | | Sets the icon in the mute slot (```show```, any other value to hide)
+| | ```speakerphone``` | | Sets the icon in the speakerphone slot (```show```, any other value to hide)
+| ```notifications``` | | | Control the notification icons
+| | ```visible``` | | ```false``` to hide the notification icons, any other value to show
+| ```clock``` | | | Control the clock display
+| | ```millis``` | | Sets the time in millis
+| | ```hhmm``` | | Sets the time in hh:mm
## Examples
Enter demo mode
@@ -90,6 +93,15 @@
```
+Show the satellite icon
+
+```
+# Sets mobile to be out-of-service, which is required for satellite to show
+adb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e level 0
+# Sets satellite to be connected
+adb shell am broadcast -a com.android.systemui.demo -e command network -e satellite show -e level 4 -e connection connected
+```
+
Show the silent volume icon
```
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index d88260f..3035481 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -16,90 +16,93 @@
package com.android.systemui.bouncer.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
-import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.time.SystemClock
-import dagger.Lazy
-import kotlinx.coroutines.test.TestScope
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class AlternateBouncerInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
private lateinit var underTest: AlternateBouncerInteractor
- private lateinit var bouncerRepository: KeyguardBouncerRepository
- private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
- private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
- @Mock private lateinit var statusBarStateController: StatusBarStateController
- @Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var systemClock: SystemClock
- @Mock private lateinit var bouncerLogger: TableLogBuffer
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
- bouncerRepository =
- KeyguardBouncerRepositoryImpl(
- FakeSystemClock(),
- TestScope().backgroundScope,
- bouncerLogger,
- )
- biometricSettingsRepository = FakeBiometricSettingsRepository()
- fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
-
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- initializeUnderTest()
- }
-
- private fun initializeUnderTest() {
- // Set any feature flags before creating the alternateBouncerInteractor
- underTest =
- AlternateBouncerInteractor(
- statusBarStateController,
- keyguardStateController,
- bouncerRepository,
- fingerprintPropertyRepository,
- biometricSettingsRepository,
- systemClock,
- keyguardUpdateMonitor,
- Lazy { mock(DeviceEntryFingerprintAuthInteractor::class.java) },
- Lazy { mock(KeyguardInteractor::class.java) },
- Lazy { mock(KeyguardTransitionInteractor::class.java) },
- TestScope().backgroundScope,
- )
+ underTest = kosmos.alternateBouncerInteractor
}
@Test(expected = IllegalStateException::class)
+ @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
underTest.show()
}
@Test
+ @DisableSceneContainer
+ fun canShowAlternateBouncer_false_dueToTransitionState() =
+ kosmos.testScope.runTest {
+ givenAlternateBouncerSupported()
+ val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ validateStep = false,
+ )
+ assertFalse(canShowAlternateBouncer!!)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun canShowAlternateBouncer_false_dueToTransitionState_scene_container() =
+ kosmos.testScope.runTest {
+ givenAlternateBouncerSupported()
+ val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
+ val isDeviceUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ assertThat(isDeviceUnlocked).isFalse()
+
+ kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
+ assertThat(isDeviceUnlocked).isTrue()
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+
+ assertThat(canShowAlternateBouncer).isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun canShowAlternateBouncerForFingerprint_givenCanShow() {
givenCanShowAlternateBouncer()
assertTrue(underTest.canShowAlternateBouncerForFingerprint())
@@ -108,7 +111,7 @@
@Test
fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() {
givenCanShowAlternateBouncer()
- bouncerRepository.setAlternateBouncerUIAvailable(false)
+ kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -116,7 +119,7 @@
@Test
fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() {
givenCanShowAlternateBouncer()
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -124,7 +127,7 @@
@Test
fun canShowAlternateBouncerForFingerprint_strongBiometricNotAllowed() {
givenCanShowAlternateBouncer()
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(false)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -132,23 +135,24 @@
@Test
fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
givenCanShowAlternateBouncer()
- whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
+ whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@Test
+ @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun show_whenCanShow() {
givenCanShowAlternateBouncer()
assertTrue(underTest.show())
- assertTrue(bouncerRepository.alternateBouncerVisible.value)
+ assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
}
@Test
fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
givenCanShowAlternateBouncer()
- whenever(keyguardStateController.isUnlocked).thenReturn(true)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -156,82 +160,86 @@
@Test
fun canShowAlternateBouncerForFingerprint_primaryBouncerShowing() {
givenCanShowAlternateBouncer()
- bouncerRepository.setPrimaryShow(true)
+ kosmos.keyguardBouncerRepository.setPrimaryShow(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@Test
+ @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun show_whenCannotShow() {
givenCannotShowAlternateBouncer()
assertFalse(underTest.show())
- assertFalse(bouncerRepository.alternateBouncerVisible.value)
+ assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
}
@Test
fun hide_wasPreviouslyShowing() {
- bouncerRepository.setAlternateVisible(true)
+ kosmos.keyguardBouncerRepository.setAlternateVisible(true)
assertTrue(underTest.hide())
- assertFalse(bouncerRepository.alternateBouncerVisible.value)
+ assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
}
@Test
fun hide_wasNotPreviouslyShowing() {
- bouncerRepository.setAlternateVisible(false)
+ kosmos.keyguardBouncerRepository.setAlternateVisible(false)
assertFalse(underTest.hide())
- assertFalse(bouncerRepository.alternateBouncerVisible.value)
+ assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
}
@Test
+ @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun canShowAlternateBouncerForFingerprint_rearFps() {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- initializeUnderTest()
givenCanShowAlternateBouncer()
- fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
+ kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@Test
+ @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun alternateBouncerUiAvailable_fromMultipleSources() {
- initializeUnderTest()
- assertFalse(bouncerRepository.alternateBouncerUIAvailable.value)
+ assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
// GIVEN there are two different sources indicating the alternate bouncer is available
underTest.setAlternateBouncerUIAvailable(true, "source1")
underTest.setAlternateBouncerUIAvailable(true, "source2")
- assertTrue(bouncerRepository.alternateBouncerUIAvailable.value)
+ assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
// WHEN one of the sources no longer says the UI is available
underTest.setAlternateBouncerUIAvailable(false, "source1")
// THEN alternate bouncer UI is still available (from the other source)
- assertTrue(bouncerRepository.alternateBouncerUIAvailable.value)
+ assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
// WHEN all sources say the UI is not available
underTest.setAlternateBouncerUIAvailable(false, "source2")
// THEN alternate boucer UI is not available
- assertFalse(bouncerRepository.alternateBouncerUIAvailable.value)
+ assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
+ }
+
+ private fun givenAlternateBouncerSupported() {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ kosmos.fingerprintPropertyRepository.supportsUdfps()
+ } else {
+ kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
+ }
}
private fun givenCanShowAlternateBouncer() {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- fingerprintPropertyRepository.supportsUdfps()
- } else {
- bouncerRepository.setAlternateBouncerUIAvailable(true)
- }
- bouncerRepository.setPrimaryShow(false)
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
- biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
- whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
- whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ givenAlternateBouncerSupported()
+ kosmos.keyguardBouncerRepository.setPrimaryShow(false)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
}
private fun givenCannotShowAlternateBouncer() {
- biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 2d78a9b..45e7d8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -39,7 +39,7 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val underTest by lazy {
- CommunalRepositoryImpl(
+ CommunalSceneRepositoryImpl(
kosmos.applicationCoroutineScope,
kosmos.sceneDataSource,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 5a7cbf6..6b896c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -21,7 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
@@ -48,7 +48,7 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var communalRepository: FakeCommunalSceneRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 83227e1..12389a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -39,7 +39,7 @@
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
@@ -48,6 +48,7 @@
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
@@ -111,7 +112,7 @@
private val testScope = kosmos.testScope
private lateinit var tutorialRepository: FakeCommunalTutorialRepository
- private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var communalRepository: FakeCommunalSceneRepository
private lateinit var mediaRepository: FakeCommunalMediaRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var smartspaceRepository: FakeSmartspaceRepository
@@ -508,30 +509,6 @@
}
@Test
- fun desiredScene_communalNotAvailable_returnsBlank() =
- testScope.runTest {
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
- val desiredScene by collectLastValue(underTest.desiredScene)
-
- underTest.changeScene(CommunalScenes.Communal)
- assertThat(desiredScene).isEqualTo(CommunalScenes.Communal)
-
- kosmos.setCommunalAvailable(false)
- runCurrent()
-
- // Scene returns blank when communal is not available.
- assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
-
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
- // After re-enabling, scene goes back to Communal.
- assertThat(desiredScene).isEqualTo(CommunalScenes.Communal)
- }
-
- @Test
fun transitionProgress_onTargetScene_fullProgress() =
testScope.runTest {
val targetScene = CommunalScenes.Blank
@@ -545,7 +522,8 @@
underTest.setTransitionState(transitionState)
// We're on the target scene.
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(targetScene))
}
@Test
@@ -563,7 +541,8 @@
underTest.setTransitionState(transitionState)
// Transition progress is still idle, but we're not on the target scene.
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(currentScene))
}
@Test
@@ -581,7 +560,8 @@
underTest.setTransitionState(transitionState)
// Progress starts at 0.
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(currentScene))
val progress = MutableStateFlow(0f)
transitionState =
@@ -599,16 +579,18 @@
// Partially transition.
progress.value = .4f
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(.4f))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Transition(.4f))
// Transition is at full progress.
progress.value = 1f
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Transition(1f))
+ assertThat(transitionProgress).isEqualTo(CommunalTransitionProgressModel.Transition(1f))
// Transition finishes.
transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene))
underTest.setTransitionState(transitionState)
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(targetScene))
}
@Test
@@ -626,7 +608,8 @@
underTest.setTransitionState(transitionState)
// Progress starts at 0.
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(currentScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(currentScene))
val progress = MutableStateFlow(0f)
transitionState =
@@ -646,16 +629,19 @@
progress.value = .4f
// This is a transition we don't care about the progress of.
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition)
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.OtherTransition)
// Transition is at full progress.
progress.value = 1f
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.OtherTransition)
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.OtherTransition)
// Transition finishes.
transitionState = MutableStateFlow(ObservableTransitionState.Idle(targetScene))
underTest.setTransitionState(transitionState)
- assertThat(transitionProgress).isEqualTo(CommunalTransitionProgress.Idle(targetScene))
+ assertThat(transitionProgress)
+ .isEqualTo(CommunalTransitionProgressModel.Idle(targetScene))
}
@Test
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 be44339..6b80775 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
@@ -28,7 +28,7 @@
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
@@ -106,7 +106,7 @@
private lateinit var userRepository: FakeUserRepository
private lateinit var shadeTestUtil: ShadeTestUtil
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
- private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var communalRepository: FakeCommunalSceneRepository
private lateinit var underTest: CommunalViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index eef2337..933f095 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -45,7 +45,7 @@
import com.android.systemui.ambient.touch.scrim.ScrimManager
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
@@ -79,7 +79,6 @@
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.isNull
-import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -156,7 +155,7 @@
@Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
- private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var communalRepository: FakeCommunalSceneRepository
@Captor var mViewCaptor: ArgumentCaptor<View>? = null
private lateinit var mService: DreamOverlayService
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
new file mode 100644
index 0000000..460a1fc
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Color
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryForegroundViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest: DeviceEntryForegroundViewModel =
+ kosmos.deviceEntryForegroundIconViewModel
+
+ @Test
+ fun aodIconColorWhite() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(true)
+ assertThat(viewModel?.tint).isEqualTo(Color.WHITE)
+ }
+
+ private fun givenUdfpsEnrolledAndEnabled() {
+ kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
+ kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 20ffa33..33e2cac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.communalRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
@@ -71,7 +71,7 @@
private val testScope = kosmos.testScope
private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
- private val communalRepository by lazy { kosmos.communalRepository }
+ private val communalRepository by lazy { kosmos.communalSceneRepository }
private val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
private val deviceEntryRepository by lazy { kosmos.fakeDeviceEntryRepository }
private val notificationsKeyguardInteractor by lazy { kosmos.notificationsKeyguardInteractor }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
index f1cd0c8..79e4fef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
@@ -179,6 +179,7 @@
val label = context.getString(R.string.status_bar_alarm)
return QSTileState(
{ Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null) },
+ R.drawable.ic_alarm,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
index 6e9db2c..a0d26c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
@@ -254,6 +254,7 @@
val label = context.getString(R.string.battery_detail_switch_title)
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
index d05e98f..ea7b7c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
@@ -78,6 +78,7 @@
val label = context.getString(R.string.quick_settings_color_correction_label)
return QSTileState(
{ Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null) },
+ R.drawable.ic_qs_color_correction,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index 3972938..b4ff565 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -245,6 +245,7 @@
): QSTileState {
return QSTileState(
{ icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) } },
+ null,
"test label",
activationState,
"test subtitle",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
index b7b3fdb..f8e01be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
@@ -66,6 +66,7 @@
null
)
},
+ R.drawable.ic_qs_font_scaling,
context.getString(R.string.quick_settings_font_scaling_label),
QSTileState.ActivationState.ACTIVE,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 39755bf..c44836a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -70,6 +70,7 @@
QSTileState.ActivationState.ACTIVE,
context.getString(R.string.quick_settings_networks_available),
Icon.Loaded(context.getDrawable(wifiRes)!!, contentDescription = null),
+ wifiRes,
context.getString(R.string.quick_settings_internet_label)
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -96,6 +97,7 @@
context.getDrawable(R.drawable.ic_qs_no_internet_unavailable)!!,
contentDescription = null
),
+ R.drawable.ic_qs_no_internet_unavailable,
context.getString(R.string.quick_settings_networks_unavailable)
)
QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
@@ -105,11 +107,13 @@
activationState: QSTileState.ActivationState,
secondaryLabel: String,
icon: Icon,
+ iconRes: Int,
contentDescription: String,
): QSTileState {
val label = context.getString(R.string.quick_settings_internet_label)
return QSTileState(
{ icon },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
index ccd7ed9..a7bd697 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
@@ -39,9 +39,7 @@
private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
private val subtitleArrayId =
SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
- private val subtitleArray by lazy {
- context.resources.getStringArray(subtitleArrayId)
- }
+ private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
// Using lazy (versus =) to make sure we override the right context -- see b/311612168
private val mapper by lazy {
ColorInversionTileMapper(
@@ -93,6 +91,7 @@
val label = context.getString(R.string.quick_settings_inversion_label)
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
index 5d2e701..75273f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
@@ -281,21 +281,16 @@
secondaryLabel: String?
): QSTileState {
val label = context.getString(R.string.quick_settings_night_display_label)
-
+ val iconRes =
+ if (activationState == QSTileState.ActivationState.ACTIVE)
+ R.drawable.qs_nightlight_icon_on
+ else R.drawable.qs_nightlight_icon_off
val contentDescription =
if (TextUtils.isEmpty(secondaryLabel)) label
else TextUtils.concat(label, ", ", secondaryLabel)
return QSTileState(
- {
- Icon.Loaded(
- context.getDrawable(
- if (activationState == QSTileState.ActivationState.ACTIVE)
- R.drawable.qs_nightlight_icon_on
- else R.drawable.qs_nightlight_icon_off
- )!!,
- null
- )
- },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
index 7ef020d..3189a9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
@@ -97,6 +97,7 @@
val label = context.getString(R.string.quick_settings_onehanded_label)
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
index d26a213..08e5cbe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -100,6 +100,7 @@
null
)
},
+ com.android.systemui.res.R.drawable.ic_qr_code_scanner,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
index 10e9bd6..ca30e9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
@@ -83,17 +83,13 @@
): QSTileState {
val label =
context.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
+ val iconRes =
+ if (activationState == QSTileState.ActivationState.ACTIVE)
+ R.drawable.qs_extra_dim_icon_on
+ else R.drawable.qs_extra_dim_icon_off
return QSTileState(
- {
- Icon.Loaded(
- context.getDrawable(
- if (activationState == QSTileState.ActivationState.ACTIVE)
- R.drawable.qs_extra_dim_icon_on
- else R.drawable.qs_extra_dim_icon_off
- )!!,
- null
- )
- },
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
context.resources
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 60c69f4..04ca38f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -172,6 +172,7 @@
val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
index d162c77..9bb6141 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -92,6 +92,7 @@
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
index 31ae9c5..336b566 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
@@ -111,6 +111,7 @@
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
index 5e7aadc..b08f39b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
@@ -147,6 +147,7 @@
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
index a977606..c021caa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
@@ -70,6 +70,7 @@
): QSTileState {
return QSTileState(
{ Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 288c083..db5921d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -118,8 +118,6 @@
underTest.setGroupExpanded(summary1, false)
// Expanding again should throw.
- // TODO(b/320238410): Remove this check when robolectric supports wtf assertions.
- Assume.assumeFalse(Build.FINGERPRINT.contains("robolectric"))
assertLogsWtf { underTest.setGroupExpanded(summary1, true) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
index fdea5a4..254a967 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/bottombar/ui/viewmodel/BottomBarViewModelTest.kt
@@ -24,13 +24,13 @@
import com.android.internal.logging.uiEventLogger
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.activityStarter
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.viewmodel.volumePanelViewModel
import com.google.common.truth.Truth.assertThat
@@ -56,7 +56,10 @@
@Captor private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
- private val kosmos = testKosmos()
+ private val kosmos =
+ testKosmos().apply {
+ volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) }
+ }
private lateinit var underTest: BottomBarViewModel
@@ -75,8 +78,7 @@
underTest.onDoneClicked()
runCurrent()
- val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState)
- assertThat(volumePanelState!!.isVisible).isFalse()
+ assertThat(volumePanelGlobalStateRepository.globalState.value.isVisible).isFalse()
}
}
}
@@ -106,8 +108,7 @@
.isEqualTo(VolumePanelUiEvent.VOLUME_PANEL_SOUND_SETTINGS_CLICKED.id)
activityStartedCaptor.value.onActivityStarted(ActivityManager.START_SUCCESS)
- val volumePanelState by collectLastValue(volumePanelViewModel.volumePanelState)
- assertThat(volumePanelState!!.isVisible).isFalse()
+ assertThat(volumePanelGlobalStateRepository.globalState.value.isVisible).isFalse()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorTest.kt
new file mode 100644
index 0000000..d44724f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumePanelGlobalStateInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val underTest = kosmos.volumePanelGlobalStateInteractor
+
+ @Test
+ fun changeVisibility_changesVisibility() =
+ with(kosmos) {
+ testScope.runTest {
+ underTest.setVisible(false)
+ assertThat(underTest.globalState.value.isVisible).isFalse()
+
+ underTest.setVisible(true)
+ assertThat(underTest.globalState.value.isVisible).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
index b37184d..d712afe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
@@ -51,7 +51,7 @@
val component5 = ComponentState(COMPONENT_5, kosmos.mockVolumePanelUiComponent, false)
val layout =
underTest.layout(
- VolumePanelState(0, false, false),
+ VolumePanelState(orientation = 0, isLargeScreen = false),
setOf(
bottomBarComponentState,
component1,
@@ -79,7 +79,7 @@
val component1State = ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false)
val component2State = ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false)
underTest.layout(
- VolumePanelState(0, false, false),
+ VolumePanelState(orientation = 0, isLargeScreen = false),
setOf(
component1State,
component2State,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index f6ada4c16..420b955 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
import com.android.systemui.volume.panel.domain.interactor.criteriaByKey
import com.android.systemui.volume.panel.domain.unavailableCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
@@ -49,7 +50,10 @@
class VolumePanelViewModelTest : SysuiTestCase() {
private val kosmos =
- testKosmos().apply { componentsLayoutManager = DefaultComponentsLayoutManager(BOTTOM_BAR) }
+ testKosmos().apply {
+ componentsLayoutManager = DefaultComponentsLayoutManager(BOTTOM_BAR)
+ volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) }
+ }
private val testableResources = context.orCreateTestableResources
@@ -58,12 +62,10 @@
@Test
fun dismissingPanel_changesVisibility() = test {
testScope.runTest {
- assertThat(underTest.volumePanelState.value.isVisible).isTrue()
-
underTest.dismissPanel()
runCurrent()
- assertThat(underTest.volumePanelState.value.isVisible).isFalse()
+ assertThat(volumePanelGlobalStateRepository.globalState.value.isVisible).isFalse()
}
}
@@ -114,11 +116,10 @@
@Test
fun dismissPanel_dismissesPanel() = test {
testScope.runTest {
- val volumePanelState by collectLastValue(underTest.volumePanelState)
underTest.dismissPanel()
runCurrent()
- assertThat(volumePanelState!!.isVisible).isFalse()
+ assertThat(volumePanelGlobalStateRepository.globalState.value.isVisible).isFalse()
}
}
@@ -126,14 +127,13 @@
fun dismissBroadcast_dismissesPanel() = test {
testScope.runTest {
runCurrent() // run the flows to let allow the receiver to be registered
- val volumePanelState by collectLastValue(underTest.volumePanelState)
broadcastDispatcher.sendIntentToMatchingReceiversOnly(
applicationContext,
Intent(DISMISS_ACTION),
)
runCurrent()
- assertThat(volumePanelState!!.isVisible).isFalse()
+ assertThat(volumePanelGlobalStateRepository.globalState.value.isVisible).isFalse()
}
}
diff --git a/packages/SystemUI/res/drawable/hearing_devices_preset_spinner_background.xml b/packages/SystemUI/res/drawable/hearing_devices_preset_spinner_background.xml
index 6e6e032..c83b6d3 100644
--- a/packages/SystemUI/res/drawable/hearing_devices_preset_spinner_background.xml
+++ b/packages/SystemUI/res/drawable/hearing_devices_preset_spinner_background.xml
@@ -30,8 +30,8 @@
android:end="20dp"
android:gravity="end|center_vertical">
<vector
- android:width="@dimen/screenrecord_spinner_arrow_size"
- android:height="@dimen/screenrecord_spinner_arrow_size"
+ android:width="@dimen/hearing_devices_preset_spinner_arrow_size"
+ android:height="@dimen/hearing_devices_preset_spinner_arrow_size"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?androidprv:attr/colorControlNormal">
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
new file mode 100644
index 0000000..1d9307b
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/hearing_devices_preset_option_text"
+ style="?android:attr/spinnerDropDownItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/hearing_devices_preset_spinner_height"
+ android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start"
+ android:gravity="center_vertical"
+ android:ellipsize="end" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
new file mode 100644
index 0000000..77172ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
@@ -0,0 +1,46 @@
+<!--
+ 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/hearing_devices_preset_spinner_height"
+ android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start"
+ android:paddingTop="@dimen/hearing_devices_preset_spinner_text_padding_vertical"
+ android:paddingBottom="@dimen/hearing_devices_preset_spinner_text_padding_vertical"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ android:lineSpacingExtra="6dp"
+ android:text="@string/hearing_devices_preset_label"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="14sp"
+ android:gravity="center_vertical"
+ android:layout_weight="1" />
+ <TextView
+ android:id="@+id/hearing_devices_preset_option_text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:textAppearance="@style/TextAppearance.Dialog.Body"
+ android:lineSpacingExtra="6dp"
+ android:gravity="center_vertical"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_weight="1" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 8e1d0a5..2bf6f80 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -17,7 +17,6 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/root"
style="@style/Widget.SliceView.Panel"
android:layout_width="wrap_content"
@@ -36,16 +35,22 @@
android:id="@+id/preset_spinner"
style="@style/BluetoothTileDialog.Device"
android:layout_width="match_parent"
- android:layout_height="@dimen/hearing_devices_preset_spinner_height"
- android:layout_marginTop="@dimen/hearing_devices_preset_spinner_margin"
- android:layout_marginBottom="@dimen/hearing_devices_preset_spinner_margin"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/hearing_devices_preset_spinner_height"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+ android:layout_marginBottom="@dimen/hearing_devices_layout_margin"
android:gravity="center_vertical"
android:background="@drawable/hearing_devices_preset_spinner_background"
android:popupBackground="@drawable/hearing_devices_preset_spinner_popup_background"
+ android:dropDownVerticalOffset="@dimen/hearing_devices_preset_spinner_height"
+ android:dropDownWidth="match_parent"
+ android:paddingStart="0dp"
+ android:paddingEnd="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/device_list"
app:layout_constraintBottom_toTopOf="@id/pair_new_device_button"
+ android:longClickable="false"
android:visibility="gone"/>
<androidx.constraintlayout.widget.Barrier
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 02b74ce..7d7a5d4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1772,10 +1772,10 @@
<dimen name="bluetooth_dialog_scroll_view_min_height_with_auto_on">350dp</dimen>
<!-- Hearing devices dialog related dimensions -->
+ <dimen name="hearing_devices_layout_margin">12dp</dimen>
<dimen name="hearing_devices_preset_spinner_height">72dp</dimen>
- <dimen name="hearing_devices_preset_spinner_margin">24dp</dimen>
<dimen name="hearing_devices_preset_spinner_text_padding_start">20dp</dimen>
- <dimen name="hearing_devices_preset_spinner_text_padding_end">80dp</dimen>
+ <dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen>
<dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen>
<dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index abfdc2a..6f2806d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -913,8 +913,10 @@
<string name="quick_settings_pair_hearing_devices">Pair new device</string>
<!-- QuickSettings: Content description of the hearing devices dialog pair new device [CHAR LIMIT=NONE] -->
<string name="accessibility_hearing_device_pair_new_device">Click to pair new device</string>
- <!-- Message when selecting hearing aids presets failed. [CHAR LIMIT=NONE] -->
+ <!-- QuickSettings: Message when selecting hearing aids presets failed. [CHAR LIMIT=NONE] -->
<string name="hearing_devices_presets_error">Couldn\'t update preset</string>
+ <!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
+ <string name="hearing_devices_preset_label">Preset</string>
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 3f34df7..3bf3fb3 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -300,6 +300,7 @@
});
mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
cancelSatelliteCollectionJob(/* reason= */ "Starting new job");
+ mLogger.logStartListeningForSatelliteCarrierText();
mSatelliteConnectionJob =
mJavaAdapter.alwaysCollectFlow(
mDeviceBasedSatelliteViewModel.getCarrierText(),
@@ -316,7 +317,7 @@
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
});
mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
- cancelSatelliteCollectionJob(/* reason= */ "Stopping listening");
+ cancelSatelliteCollectionJob(/* reason= */ "#handleSetListening has null callback");
}
}
@@ -336,6 +337,7 @@
private void onSatelliteCarrierTextChanged(@Nullable String text) {
mLogger.logUpdateCarrierTextForReason(REASON_SATELLITE_CHANGED);
+ mLogger.logNewSatelliteCarrierText(text);
mSatelliteCarrierText = text;
updateCarrierText();
}
@@ -654,6 +656,7 @@
private void cancelSatelliteCollectionJob(String reason) {
Job job = mSatelliteConnectionJob;
if (job != null) {
+ mLogger.logStopListeningForSatelliteCarrierText(reason);
job.cancel(new CancellationException(reason));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
index 48fea55..7d0c491 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
@@ -38,8 +38,11 @@
buffer.log(
TAG,
LogLevel.VERBOSE,
- { int1 = numSubs },
- { "updateCarrierText: location=${location ?: "(unknown)"} numSubs=$int1" },
+ {
+ int1 = numSubs
+ str1 = location
+ },
+ { "updateCarrierText: location=${str1 ?: "(unknown)"} numSubs=$int1" },
)
}
@@ -77,6 +80,15 @@
)
}
+ fun logNewSatelliteCarrierText(newSatelliteText: String?) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ { str1 = newSatelliteText },
+ { "New satellite text = $str1" },
+ )
+ }
+
fun logUsingSatelliteText(satelliteText: String) {
buffer.log(
TAG,
@@ -125,10 +137,37 @@
buffer.log(
TAG,
LogLevel.DEBUG,
- { int1 = reason },
+ {
+ int1 = reason
+ str1 = location
+ },
{
"refreshing carrier info for reason: ${reason.reasonMessage()}" +
- " location=${location ?: "(unknown)"}"
+ " location=${str1 ?: "(unknown)"}"
+ }
+ )
+ }
+
+ fun logStartListeningForSatelliteCarrierText() {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = location },
+ { "Start listening for satellite carrier text. Location=${str1 ?: "(unknown)"}" }
+ )
+ }
+
+ fun logStopListeningForSatelliteCarrierText(reason: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = location
+ str2 = reason
+ },
+ {
+ "Stop listening for satellite carrier text. " +
+ "Location=${str1 ?: "(unknown)"} Reason=$str2"
}
)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index ce4032a..bebfd85 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,7 +16,6 @@
package com.android.keyguard.logging
-import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
@@ -81,6 +80,23 @@
)
}
+ fun delayShowingTrustAgentError(
+ msg: CharSequence,
+ fpEngaged: Boolean,
+ faceRunning: Boolean,
+ ) {
+ buffer.log(
+ BIO_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = msg.toString()
+ bool1 = fpEngaged
+ bool2 = faceRunning
+ },
+ { "Delay showing trustAgentError:$str1. fpEngaged:$bool1 faceRunning:$bool2 " }
+ )
+ }
+
fun logUpdateDeviceEntryIndication(
animate: Boolean,
visible: Boolean,
@@ -118,10 +134,9 @@
)
}
- fun logDropNonFingerprintMessage(
+ fun logDropFaceMessage(
message: CharSequence,
followUpMessage: CharSequence?,
- biometricSourceType: BiometricSourceType?,
) {
buffer.log(
KeyguardIndicationController.TAG,
@@ -129,12 +144,8 @@
{
str1 = message.toString()
str2 = followUpMessage?.toString()
- str3 = biometricSourceType?.name
},
- {
- "droppingNonFingerprintMessage message=$str1 " +
- "followUpMessage:$str2 biometricSourceType:$str3"
- }
+ { "droppingFaceMessage message=$str1 followUpMessage:$str2" }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 7b5a09c..28dd233 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -32,6 +32,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.Visibility;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
@@ -208,6 +209,10 @@
}
mMainHandler.post(() -> {
mDeviceListAdapter.refreshDeviceItemList(mHearingDeviceItemList);
+ final List<BluetoothHapPresetInfo> presetInfos =
+ mPresetsController.getAllPresetInfo();
+ final int activePresetIndex = mPresetsController.getActivePresetIndex();
+ refreshPresetInfoAdapter(presetInfos, activePresetIndex);
mPresetSpinner.setVisibility(
(activeHearingDevice != null && !mPresetInfoAdapter.isEmpty()) ? VISIBLE
: GONE);
@@ -295,10 +300,23 @@
mHearingDeviceItemList);
mPresetsController.setActiveHearingDevice(activeHearingDevice);
- mPresetInfoAdapter = new ArrayAdapter<>(dialog.getContext(),
- android.R.layout.simple_spinner_dropdown_item);
- mPresetInfoAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mPresetInfoAdapter = new ArrayAdapter<String>(dialog.getContext(),
+ R.layout.hearing_devices_preset_spinner_selected,
+ R.id.hearing_devices_preset_option_text);
+ mPresetInfoAdapter.setDropDownViewResource(
+ R.layout.hearing_devices_preset_dropdown_item);
mPresetSpinner.setAdapter(mPresetInfoAdapter);
+
+ // disable redundant Touch & Hold accessibility action for Switch Access
+ mPresetSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 2e29c3b..7503a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -46,15 +46,22 @@
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
- viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
+ viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
+ Log.d(
+ "UdfpsTouchOverlayBinder",
+ "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+ )
+ view.isInvisible = !shouldHandleTouches
+ udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
+ }
+ }
+ .invokeOnCompletion {
Log.d(
"UdfpsTouchOverlayBinder",
- "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+ "[$view-detached]: update shouldHandleTouches=false"
)
- view.isInvisible = !shouldHandleTouches
- udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
+ udfpsOverlayInteractor.setHandleTouches(false)
}
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index e0334a0..1d11dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -28,6 +28,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.time.SystemClock
@@ -60,6 +63,7 @@
private val deviceEntryFingerprintAuthInteractor: Lazy<DeviceEntryFingerprintAuthInteractor>,
private val keyguardInteractor: Lazy<KeyguardInteractor>,
keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>,
+ sceneInteractor: Lazy<SceneInteractor>,
@Application scope: CoroutineScope,
) {
var receivedDownTouch = false
@@ -96,30 +100,42 @@
alternateBouncerSupported
.flatMapLatest { alternateBouncerSupported ->
if (alternateBouncerSupported) {
- keyguardTransitionInteractor.get().currentKeyguardState.flatMapLatest {
- currentKeyguardState ->
- if (currentKeyguardState == KeyguardState.GONE) {
- flowOf(false)
- } else {
- combine(
- deviceEntryFingerprintAuthInteractor
- .get()
- .isFingerprintAuthCurrentlyAllowed,
- keyguardInteractor.get().isKeyguardDismissible,
- bouncerRepository.primaryBouncerShow,
- isDozingOrAod
+ combine(
+ keyguardTransitionInteractor.get().currentKeyguardState,
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().currentScene
+ } else {
+ flowOf(Scenes.Lockscreen)
+ },
+ ::Pair
+ )
+ .flatMapLatest { (currentKeyguardState, transitionState) ->
+ if (currentKeyguardState == KeyguardState.GONE) {
+ flowOf(false)
+ } else if (
+ SceneContainerFlag.isEnabled && transitionState == Scenes.Gone
) {
- fingerprintAllowed,
- keyguardDismissible,
- primaryBouncerShowing,
- dozing ->
- fingerprintAllowed &&
- !keyguardDismissible &&
- !primaryBouncerShowing &&
- !dozing
+ flowOf(false)
+ } else {
+ combine(
+ deviceEntryFingerprintAuthInteractor
+ .get()
+ .isFingerprintAuthCurrentlyAllowed,
+ keyguardInteractor.get().isKeyguardDismissible,
+ bouncerRepository.primaryBouncerShow,
+ isDozingOrAod
+ ) {
+ fingerprintAllowed,
+ keyguardDismissible,
+ primaryBouncerShowing,
+ dozing ->
+ fingerprintAllowed &&
+ !keyguardDismissible &&
+ !primaryBouncerShowing &&
+ !dozing
+ }
}
}
- }
} else {
flowOf(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
index 1de3459..7f137f3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -21,5 +21,5 @@
@Module
interface CommunalRepositoryModule {
- @Binds fun communalRepository(impl: CommunalRepositoryImpl): CommunalRepository
+ @Binds fun communalRepository(impl: CommunalSceneRepositoryImpl): CommunalSceneRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
rename to packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index 8bfd8d9..d6d08b4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -36,7 +36,7 @@
import kotlinx.coroutines.flow.stateIn
/** Encapsulates the state of communal mode. */
-interface CommunalRepository {
+interface CommunalSceneRepository {
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
*/
@@ -48,6 +48,9 @@
/** Updates the requested scene. */
fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null)
+ /** Immediately snaps to the desired scene. */
+ fun snapToScene(toScene: SceneKey)
+
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
@@ -58,12 +61,12 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
-class CommunalRepositoryImpl
+class CommunalSceneRepositoryImpl
@Inject
constructor(
@Background backgroundScope: CoroutineScope,
@Communal private val sceneDataSource: SceneDataSource,
-) : CommunalRepository {
+) : CommunalSceneRepository {
override val currentScene: StateFlow<SceneKey> = sceneDataSource.currentScene
@@ -82,6 +85,10 @@
sceneDataSource.changeScene(toScene, transitionKey)
}
+ override fun snapToScene(toScene: SceneKey) {
+ sceneDataSource.snapToScene(toScene)
+ }
+
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9599a88..2be28ca 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -29,7 +29,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalPrefsRepository
-import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
@@ -97,7 +96,6 @@
@Application val applicationScope: CoroutineScope,
@Background val bgDispatcher: CoroutineDispatcher,
broadcastDispatcher: BroadcastDispatcher,
- private val communalRepository: CommunalRepository,
private val widgetRepository: CommunalWidgetRepository,
private val communalPrefsRepository: CommunalPrefsRepository,
mediaRepository: CommunalMediaRepository,
@@ -110,6 +108,7 @@
private val userTracker: UserTracker,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
+ private val communalSceneInteractor: CommunalSceneInteractor,
sceneInteractor: SceneInteractor,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
@@ -174,15 +173,19 @@
*
* If [isCommunalAvailable] is false, will return [CommunalScenes.Blank]
*/
- val desiredScene: Flow<SceneKey> =
- communalRepository.currentScene.combine(isCommunalAvailable) { scene, available ->
- if (available) scene else CommunalScenes.Blank
- }
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ val desiredScene: Flow<SceneKey> = communalSceneInteractor.currentScene
/** Transition state of the hub mode. */
- val transitionState: StateFlow<ObservableTransitionState> = communalRepository.transitionState
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ val transitionState: StateFlow<ObservableTransitionState> =
+ communalSceneInteractor.transitionState
- val _userActivity: MutableSharedFlow<Unit> =
+ private val _userActivity: MutableSharedFlow<Unit> =
MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val userActivity: Flow<Unit> = _userActivity.asSharedFlow()
@@ -212,32 +215,18 @@
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
- fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
- communalRepository.setTransitionState(transitionState)
- }
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) =
+ communalSceneInteractor.setTransitionState(transitionState)
/** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
fun transitionProgressToScene(targetScene: SceneKey) =
- transitionState
- .flatMapLatest { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- flowOf(CommunalTransitionProgress.Idle(state.currentScene))
- is ObservableTransitionState.Transition ->
- if (state.toScene == targetScene) {
- state.progress.map {
- CommunalTransitionProgress.Transition(
- // Clamp the progress values between 0 and 1 as actual progress
- // values can be higher than 0 or lower than 1 due to a fling.
- progress = it.coerceIn(0.0f, 1.0f)
- )
- }
- } else {
- flowOf(CommunalTransitionProgress.OtherTransition)
- }
- }
- }
- .distinctUntilChanged()
+ communalSceneInteractor.transitionProgressToScene(targetScene)
/**
* Flow that emits a boolean if the communal UI is the target scene, ie. the [desiredScene] is
@@ -283,34 +272,30 @@
* This will not be true while transitioning to the hub and will turn false immediately when a
* swipe to exit the hub starts.
*/
- val isIdleOnCommunal: StateFlow<Boolean> =
- communalRepository.transitionState
- .map {
- it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Communal
- }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Eagerly,
- initialValue = false,
- )
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ val isIdleOnCommunal: StateFlow<Boolean> = communalSceneInteractor.isIdleOnCommunal
/**
* Flow that emits a boolean if any portion of the communal UI is visible at all.
*
* This flow will be true during any transition and when idle on the communal scene.
*/
- val isCommunalVisible: Flow<Boolean> =
- communalRepository.transitionState.map {
- !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
- }
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ val isCommunalVisible: Flow<Boolean> = communalSceneInteractor.isCommunalVisible
/**
* Asks for an asynchronous scene witch to [newScene], which will use the corresponding
* installed transition or the one specified by [transitionKey], if provided.
*/
- fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) {
- communalRepository.changeScene(newScene, transitionKey)
- }
+ @Deprecated(
+ "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
+ )
+ fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) =
+ communalSceneInteractor.changeScene(newScene, transitionKey)
fun setEditModeOpen(isOpen: Boolean) {
_editModeOpen.value = isOpen
@@ -579,17 +564,3 @@
}
}
}
-
-/** Simplified transition progress data class for tracking a single transition between scenes. */
-sealed class CommunalTransitionProgress {
- /** No transition/animation is currently running. */
- data class Idle(val scene: SceneKey) : CommunalTransitionProgress()
-
- /** There is a transition animating to the expected scene. */
- data class Transition(
- val progress: Float,
- ) : CommunalTransitionProgress()
-
- /** There is a transition animating to a scene other than the expected scene. */
- data object OtherTransition : CommunalTransitionProgress()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
new file mode 100644
index 0000000..5cfe979
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.domain.interactor
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.communal.data.repository.CommunalSceneRepository
+import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+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.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalSceneInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val communalSceneRepository: CommunalSceneRepository,
+) {
+ /**
+ * Asks for an asynchronous scene witch to [newScene], which will use the corresponding
+ * installed transition or the one specified by [transitionKey], if provided.
+ */
+ fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) {
+ communalSceneRepository.changeScene(newScene, transitionKey)
+ }
+
+ /** Immediately snaps to the new scene. */
+ fun snapToScene(newScene: SceneKey) {
+ communalSceneRepository.snapToScene(newScene)
+ }
+
+ /**
+ * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
+ */
+ val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene
+
+ /** Transition state of the hub mode. */
+ val transitionState: StateFlow<ObservableTransitionState> =
+ communalSceneRepository.transitionState
+
+ /**
+ * Updates the transition state of the hub [SceneTransitionLayout].
+ *
+ * Note that you must call is with `null` when the UI is done or risk a memory leak.
+ */
+ fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
+ communalSceneRepository.setTransitionState(transitionState)
+ }
+
+ /** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
+ fun transitionProgressToScene(targetScene: SceneKey) =
+ transitionState
+ .flatMapLatest { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ flowOf(CommunalTransitionProgressModel.Idle(state.currentScene))
+ is ObservableTransitionState.Transition ->
+ if (state.toScene == targetScene) {
+ state.progress.map {
+ CommunalTransitionProgressModel.Transition(
+ // Clamp the progress values between 0 and 1 as actual progress
+ // values can be higher than 0 or lower than 1 due to a fling.
+ progress = it.coerceIn(0.0f, 1.0f)
+ )
+ }
+ } else {
+ flowOf(CommunalTransitionProgressModel.OtherTransition)
+ }
+ }
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Flow that emits a boolean if the communal UI is fully visible and not in transition.
+ *
+ * This will not be true while transitioning to the hub and will turn false immediately when a
+ * swipe to exit the hub starts.
+ */
+ val isIdleOnCommunal: StateFlow<Boolean> =
+ transitionState
+ .map {
+ it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Communal
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ /**
+ * Flow that emits a boolean if any portion of the communal UI is visible at all.
+ *
+ * This flow will be true during any transition and when idle on the communal scene.
+ */
+ val isCommunalVisible: Flow<Boolean> =
+ transitionState.map {
+ !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalTransitionProgressModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalTransitionProgressModel.kt
new file mode 100644
index 0000000..e3187c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalTransitionProgressModel.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.domain.model
+
+import com.android.compose.animation.scene.SceneKey
+
+/** Simplified transition progress data class for tracking a single transition between scenes. */
+sealed interface CommunalTransitionProgressModel {
+ /** No transition/animation is currently running. */
+ data class Idle(val scene: SceneKey) : CommunalTransitionProgressModel
+
+ /** There is a transition animating to the expected scene. */
+ data class Transition(
+ val progress: Float,
+ ) : CommunalTransitionProgressModel
+
+ /** There is a transition animating to a scene other than the expected scene. */
+ data object OtherTransition : CommunalTransitionProgressModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index a3c61a4..73cfb52 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -26,4 +26,6 @@
object CommunalTransitionKeys {
/** Fades the glanceable hub without any translation */
val SimpleFade = TransitionKey("SimpleFade")
+ /** Immediately transitions without any delay */
+ val Immediately = TransitionKey("Immediately")
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
index b7e8205..058ca4d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -83,7 +83,9 @@
override fun allocateAppWidgetId(): Int {
return super.allocateAppWidgetId().also { appWidgetId ->
backgroundScope.launch {
- observers.forEach { observer -> observer.onAllocateAppWidgetId(appWidgetId) }
+ synchronized(observers) {
+ observers.forEach { observer -> observer.onAllocateAppWidgetId(appWidgetId) }
+ }
}
}
}
@@ -91,18 +93,28 @@
override fun deleteAppWidgetId(appWidgetId: Int) {
super.deleteAppWidgetId(appWidgetId)
backgroundScope.launch {
- observers.forEach { observer -> observer.onDeleteAppWidgetId(appWidgetId) }
+ synchronized(observers) {
+ observers.forEach { observer -> observer.onDeleteAppWidgetId(appWidgetId) }
+ }
}
}
override fun startListening() {
super.startListening()
- backgroundScope.launch { observers.forEach { observer -> observer.onHostStartListening() } }
+ backgroundScope.launch {
+ synchronized(observers) {
+ observers.forEach { observer -> observer.onHostStartListening() }
+ }
+ }
}
override fun stopListening() {
super.stopListening()
- backgroundScope.launch { observers.forEach { observer -> observer.onHostStopListening() } }
+ backgroundScope.launch {
+ synchronized(observers) {
+ observers.forEach { observer -> observer.onHostStopListening() }
+ }
+ }
}
fun addObserver(observer: Observer) {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index ec574d2..a5eafa9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -28,6 +28,7 @@
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
@@ -43,9 +44,15 @@
biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
fingerprintPropertyRepository: FingerprintPropertyRepository,
) {
- /** Whether fingerprint authentication is currently running or not */
+ /**
+ * Whether fingerprint authentication is currently running or not. This does not mean the user
+ * [isEngaged] with the fingerprint.
+ */
val isRunning: Flow<Boolean> = repository.isRunning
+ /** Whether the user is actively engaging with the fingerprint sensor */
+ val isEngaged: StateFlow<Boolean> = repository.isEngaged
+
/** Provide the current status of fingerprint authentication. */
val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
repository.authenticationStatus
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 9cdba58..f2a544e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -77,7 +77,9 @@
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
@@ -101,6 +103,7 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -116,6 +119,7 @@
private final DisplayTracker mDisplayTracker;
private final PowerInteractor mPowerInteractor;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private final Executor mMainExecutor;
private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
@@ -308,6 +312,7 @@
}
private final WindowManagerOcclusionManager mWmOcclusionManager;
+ private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
@Override
@@ -331,7 +336,9 @@
FeatureFlags featureFlags,
PowerInteractor powerInteractor,
WindowManagerOcclusionManager windowManagerOcclusionManager,
- Lazy<SceneInteractor> sceneInteractorLazy) {
+ Lazy<SceneInteractor> sceneInteractorLazy,
+ @Main Executor mainExecutor,
+ KeyguardEnabledInteractor keyguardEnabledInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -341,6 +348,7 @@
mFlags = featureFlags;
mPowerInteractor = powerInteractor;
mSceneInteractorLazy = sceneInteractorLazy;
+ mMainExecutor = mainExecutor;
if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -355,6 +363,7 @@
}
mWmOcclusionManager = windowManagerOcclusionManager;
+ mKeyguardEnabledInteractor = keyguardEnabledInteractor;
}
@Override
@@ -593,6 +602,7 @@
public void setKeyguardEnabled(boolean enabled) {
trace("setKeyguardEnabled enabled" + enabled);
checkPermission();
+ mKeyguardEnabledInteractor.notifyKeyguardEnabled(enabled);
mKeyguardViewMediator.setKeyguardEnabled(enabled);
}
@@ -619,8 +629,8 @@
mKeyguardViewMediator.showDismissibleKeyguard();
if (SceneContainerFlag.isEnabled() && mFoldGracePeriodProvider.get().isEnabled()) {
- mSceneInteractorLazy.get().changeScene(
- Scenes.Lockscreen, "KeyguardService.showDismissibleKeyguard");
+ mMainExecutor.execute(() -> mSceneInteractorLazy.get().changeScene(
+ Scenes.Lockscreen, "KeyguardService.showDismissibleKeyguard"));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 0ebc92e..b1589da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -40,9 +40,13 @@
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
@@ -57,6 +61,9 @@
*/
val isRunning: Flow<Boolean>
+ /** Whether the fingerprint sensor is actively authenticating. */
+ val isEngaged: StateFlow<Boolean>
+
/**
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/
@@ -176,6 +183,17 @@
mainDispatcher
) // keyguardUpdateMonitor requires registration on main thread.
+ override val isEngaged: StateFlow<Boolean> =
+ authenticationStatus
+ .map { it.isEngaged }
+ .filterNotNull()
+ .map { it }
+ .stateIn(
+ scope = scope,
+ started = WhileSubscribed(),
+ initialValue = false,
+ )
+
// TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages
// in BiometricStatusRepository
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 8a53dd1..a2bbcad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -109,6 +109,19 @@
)
val isKeyguardGoingAway: Flow<Boolean>
+ /**
+ * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
+ * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ *
+ * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
+ * permission to do so (such as Phone).
+ *
+ * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
+ * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
+ * locked when it was disabled.
+ */
+ val isKeyguardEnabled: StateFlow<Boolean>
+
/** Is the always-on display available to be used? */
val isAodAvailable: StateFlow<Boolean>
@@ -269,6 +282,9 @@
"'keyguardDoneAnimationsFinished' is when the GONE transition is finished."
)
fun keyguardDoneAnimationsFinished()
+
+ /** Sets whether the keyguard is enabled (see [isKeyguardEnabled]). */
+ fun setKeyguardEnabled(enabled: Boolean)
}
/** Encapsulates application state for the keyguard. */
@@ -439,6 +455,9 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
+ private val _isKeyguardEnabled = MutableStateFlow(true)
+ override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
+
private val _isDozing = MutableStateFlow(statusBarStateController.isDozing)
override val isDozing: StateFlow<Boolean> = _isDozing.asStateFlow()
@@ -664,6 +683,10 @@
_clockShouldBeCentered.value = shouldBeCentered
}
+ override fun setKeyguardEnabled(enabled: Boolean) {
+ _isKeyguardEnabled.value = enabled
+ }
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index a306954..01109af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
@@ -50,6 +51,7 @@
private val keyguardInteractor: KeyguardInteractor,
powerInteractor: PowerInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ val deviceEntryRepository: DeviceEntryRepository,
) :
TransitionInteractor(
fromState = KeyguardState.AOD,
@@ -125,7 +127,12 @@
val shouldTransitionToOccluded =
!KeyguardWmStateRefactor.isEnabled && isKeyguardOccludedLegacy
- if (canDismissLockscreen) {
+ val shouldTransitionToGone =
+ (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) ||
+ (KeyguardWmStateRefactor.isEnabled &&
+ !deviceEntryRepository.isLockscreenEnabled())
+
+ if (shouldTransitionToGone) {
startTransitionTo(
toState = KeyguardState.GONE,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 115fc36..7d3de30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
@@ -50,6 +51,7 @@
powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ val deviceEntryRepository: DeviceEntryRepository,
) :
TransitionInteractor(
fromState = KeyguardState.DOZING,
@@ -99,7 +101,9 @@
canTransitionToGoneOnWake,
primaryBouncerShowing) ->
startTransitionTo(
- if (isWakeAndUnlock(biometricUnlockState.mode)) {
+ if (!deviceEntryRepository.isLockscreenEnabled()) {
+ KeyguardState.GONE
+ } else if (isWakeAndUnlock(biometricUnlockState.mode)) {
KeyguardState.GONE
} else if (canTransitionToGoneOnWake) {
KeyguardState.GONE
@@ -145,7 +149,12 @@
!isWakeAndUnlock(biometricUnlockState.mode)
) {
startTransitionTo(
- if (canDismissLockscreen) {
+ if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) {
+ KeyguardState.GONE
+ } else if (
+ KeyguardWmStateRefactor.isEnabled &&
+ !deviceEntryRepository.isLockscreenEnabled()
+ ) {
KeyguardState.GONE
} else if (primaryBouncerShowing) {
KeyguardState.PRIMARY_BOUNCER
@@ -153,7 +162,8 @@
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
- }
+ },
+ ownerReason = "waking from dozing"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 2b3732f..8ca29c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -36,6 +37,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@SysUISingleton
@@ -52,6 +54,8 @@
private val communalInteractor: CommunalInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
private val biometricSettingsRepository: BiometricSettingsRepository,
+ private val keyguardRepository: KeyguardRepository,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.GONE,
@@ -93,6 +97,21 @@
startTransitionTo(to, ownerReason = "User initiated lockdown")
}
}
+
+ scope.launch {
+ keyguardRepository.isKeyguardEnabled
+ .filterRelevantKeyguardStateAnd { enabled -> enabled }
+ .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+ .filter { reshow -> reshow }
+ .collect {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason =
+ "Keyguard was re-enabled, and we weren't GONE when it " +
+ "was originally disabled"
+ )
+ }
+ }
} else {
scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
keyguardInteractor.isKeyguardShowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index fcf67d5..af1ce2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -19,7 +19,7 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalTransitionProgress
+import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -71,7 +71,7 @@
if (id == null) {
// No transition started.
if (
- transitionProgress is CommunalTransitionProgress.Transition &&
+ transitionProgress is CommunalTransitionProgressModel.Transition &&
lastStartedState == fromState
) {
transitionId =
@@ -93,7 +93,7 @@
val nextState: TransitionState
val progressFraction: Float
when (transitionProgress) {
- is CommunalTransitionProgress.Idle -> {
+ is CommunalTransitionProgressModel.Idle -> {
if (transitionProgress.scene == toScene) {
nextState = TransitionState.FINISHED
progressFraction = 1f
@@ -102,11 +102,11 @@
progressFraction = 0f
}
}
- is CommunalTransitionProgress.Transition -> {
+ is CommunalTransitionProgressModel.Transition -> {
nextState = TransitionState.RUNNING
progressFraction = transitionProgress.progress
}
- is CommunalTransitionProgress.OtherTransition -> {
+ is CommunalTransitionProgressModel.OtherTransition -> {
// Shouldn't happen but if another transition starts during the
// current one, mark the current one as canceled.
nextState = TransitionState.CANCELED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
new file mode 100644
index 0000000..8dede01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not
+ * enabled, the lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ *
+ * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
+ * permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart
+ * rather than simply dismissing the keyguard or setting up the device to have Security: None, for
+ * reasons unknown.
+ */
+@SysUISingleton
+class KeyguardEnabledInteractor
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ val repository: KeyguardRepository,
+ val biometricSettingsRepository: BiometricSettingsRepository,
+ transitionInteractor: KeyguardTransitionInteractor,
+) {
+
+ init {
+ /**
+ * Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already
+ * GONE.
+ */
+ scope.launch {
+ repository.isKeyguardEnabled
+ .filter { enabled -> !enabled }
+ .sampleCombine(
+ biometricSettingsRepository.isCurrentUserInLockdown,
+ transitionInteractor.currentTransitionInfoInternal,
+ )
+ .collect { (_, inLockdown, currentTransitionInfo) ->
+ if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
+ transitionInteractor.startDismissKeyguardTransition("keyguard disabled")
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
+ * became disabled.
+ */
+ val showKeyguardWhenReenabled: Flow<Boolean> =
+ repository.isKeyguardEnabled
+ // Whenever the keyguard is disabled...
+ .filter { enabled -> !enabled }
+ .sampleCombine(
+ transitionInteractor.currentTransitionInfoInternal,
+ biometricSettingsRepository.isCurrentUserInLockdown
+ )
+ .map { (_, transitionInfo, inLockdown) ->
+ // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
+ // we want to remember that and re-show it when keyguard is enabled again.
+ transitionInfo.to != KeyguardState.GONE && !inLockdown
+ }
+
+ fun notifyKeyguardEnabled(enabled: Boolean) {
+ repository.setKeyguardEnabled(enabled)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 8ba09bd..fb65a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -176,7 +176,8 @@
if (!returningToGoneAfterCancellation) {
// By default, apply the lockscreen visibility of the current state.
- KeyguardState.lockscreenVisibleInState(currentState)
+ deviceEntryInteractor.get().isLockscreenEnabled() &&
+ KeyguardState.lockscreenVisibleInState(currentState)
} else {
// If we're transitioning to GONE after a prior canceled transition from
// GONE, then this is the camera launch transition from an asleep state back
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
index d8b7b4a..e92dec0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -19,6 +19,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
import android.hardware.fingerprint.FingerprintManager
import android.os.SystemClock.elapsedRealtime
import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -26,26 +27,43 @@
/**
* Fingerprint authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
+ *
+ * @isEngaged whether fingerprint is actively engaged by the user. This is distinct from fingerprint
+ * running on the device. Can be null if the status does not have an associated isEngaged state.
*/
-sealed class FingerprintAuthenticationStatus
+sealed class FingerprintAuthenticationStatus(val isEngaged: Boolean?)
/** Fingerprint authentication success status. */
data class SuccessFingerprintAuthenticationStatus(
val userId: Int,
val isStrongBiometric: Boolean,
-) : FingerprintAuthenticationStatus()
+) : FingerprintAuthenticationStatus(isEngaged = false)
/** Fingerprint authentication help message. */
data class HelpFingerprintAuthenticationStatus(
val msgId: Int,
val msg: String?,
-) : FingerprintAuthenticationStatus()
+) : FingerprintAuthenticationStatus(isEngaged = null)
/** Fingerprint acquired message. */
data class AcquiredFingerprintAuthenticationStatus(
val authenticationReason: AuthenticationReason,
val acquiredInfo: Int
-) : FingerprintAuthenticationStatus() {
+) :
+ FingerprintAuthenticationStatus(
+ isEngaged =
+ if (acquiredInfo == FINGERPRINT_ACQUIRED_START) {
+ true
+ } else if (
+ acquiredInfo == FINGERPRINT_ACQUIRED_UNKNOWN ||
+ acquiredInfo == FINGERPRINT_ACQUIRED_GOOD
+ ) {
+ null
+ } else {
+ // soft errors that indicate fingerprint activity ended
+ false
+ }
+ ) {
val fingerprintCaptureStarted: Boolean = acquiredInfo == FINGERPRINT_ACQUIRED_START
@@ -53,7 +71,8 @@
}
/** Fingerprint authentication failed message. */
-data object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+data object FailFingerprintAuthenticationStatus :
+ FingerprintAuthenticationStatus(isEngaged = false)
/** Fingerprint authentication error message */
data class ErrorFingerprintAuthenticationStatus(
@@ -61,7 +80,7 @@
val msg: String? = null,
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime(),
-) : FingerprintAuthenticationStatus() {
+) : FingerprintAuthenticationStatus(isEngaged = false) {
fun isCancellationError(): Boolean =
msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED ||
msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 200d30c..7a2e610 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -88,6 +88,12 @@
}
}
+ /**
+ * Setups different icon states.
+ * - All lottie views will require a LottieOnCompositionLoadedListener to update
+ * LottieProperties (like color) of the view.
+ * - Drawable properties can be updated using ImageView properties like imageTintList.
+ */
private fun setupIconStates() {
// Lockscreen States
// LOCK
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 0aa6d12..0f1f5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -64,13 +64,6 @@
}
}
- private val color: Flow<Int> =
- deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection ->
- configurationInteractor.onAnyConfigurationChange
- .map { getColor(useBgProtection) }
- .onStart { emit(getColor(useBgProtection)) }
- }
-
// While dozing, the display can show the AOD UI; show the AOD udfps when dozing
private val useAodIconVariant: Flow<Boolean> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfspEnrolled ->
@@ -81,6 +74,22 @@
}
}
+ private val color: Flow<Int> =
+ useAodIconVariant
+ .flatMapLatest { useAodVariant ->
+ if (useAodVariant) {
+ flowOf(android.graphics.Color.WHITE)
+ } else {
+ deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection
+ ->
+ configurationInteractor.onAnyConfigurationChange
+ .map { getColor(useBgProtection) }
+ .onStart { emit(getColor(useBgProtection)) }
+ }
+ }
+ }
+ .distinctUntilChanged()
+
private val padding: Flow<Int> =
deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
if (udfpsSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 5babc8b..14890d7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -458,7 +458,7 @@
@SysUISingleton
@CarrierTextManagerLog
public static LogBuffer provideCarrierTextManagerLog(LogBufferFactory factory) {
- return factory.create("CarrierTextManagerLog", 100);
+ return factory.create("CarrierTextManagerLog", 400);
}
/**
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 9487085..d0f8412 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE;
import static android.view.InputDevice.SOURCE_MOUSE;
import static android.view.InputDevice.SOURCE_TOUCHPAD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
@@ -24,10 +25,13 @@
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
+import static java.util.stream.Collectors.joining;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
@@ -47,6 +51,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.provider.DeviceConfig;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -102,6 +107,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
@@ -255,7 +261,7 @@
private boolean mIsAttached;
private boolean mIsGestureHandlingEnabled;
- private boolean mIsTrackpadConnected;
+ private final Set<Integer> mTrackpadsConnected = new ArraySet<>();
private boolean mInGestureNavMode;
private boolean mUsingThreeButtonNav;
private boolean mIsEnabled;
@@ -358,16 +364,14 @@
private final InputManager.InputDeviceListener mInputDeviceListener =
new InputManager.InputDeviceListener() {
-
- // Only one trackpad can be connected to a device at a time, since it takes over the
- // only USB port.
- private int mTrackpadDeviceId;
-
@Override
public void onInputDeviceAdded(int deviceId) {
if (isTrackpadDevice(deviceId)) {
- mTrackpadDeviceId = deviceId;
- update(true /* isTrackpadConnected */);
+ boolean wasEmpty = mTrackpadsConnected.isEmpty();
+ mTrackpadsConnected.add(deviceId);
+ if (wasEmpty) {
+ update();
+ }
}
}
@@ -376,18 +380,29 @@
@Override
public void onInputDeviceRemoved(int deviceId) {
- if (mTrackpadDeviceId == deviceId) {
- update(false /* isTrackpadConnected */);
+ mTrackpadsConnected.remove(deviceId);
+ if (mTrackpadsConnected.isEmpty()) {
+ update();
}
}
- private void update(boolean isTrackpadConnected) {
- boolean isPreviouslyTrackpadConnected = mIsTrackpadConnected;
- mIsTrackpadConnected = isTrackpadConnected;
- if (isPreviouslyTrackpadConnected != mIsTrackpadConnected) {
- updateIsEnabled();
- updateCurrentUserResources();
+ private void update() {
+ if (mIsEnabled && !mTrackpadsConnected.isEmpty()) {
+ // Don't reinitialize gesture handling due to trackpad connecting when it's
+ // already set up.
+ return;
}
+ updateIsEnabled();
+ updateCurrentUserResources();
+ }
+
+ private boolean isTrackpadDevice(int deviceId) {
+ InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ if (inputDevice == null) {
+ return false;
+ }
+ return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE
+ | InputDevice.SOURCE_TOUCHPAD);
}
};
@@ -566,6 +581,7 @@
mOverviewProxyService.removeCallback(mQuickSwitchListener);
mSysUiState.removeCallback(mSysUiStateCallback);
mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
+ mTrackpadsConnected.clear();
updateIsEnabled();
mUserTracker.removeCallback(mUserChangedCallback);
}
@@ -605,7 +621,7 @@
Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
mIsGestureHandlingEnabled = mInGestureNavMode || (mUsingThreeButtonNav
- && mIsTrackpadConnected);
+ && !mTrackpadsConnected.isEmpty());
boolean isEnabled = mIsAttached && mIsGestureHandlingEnabled;
if (isEnabled == mIsEnabled) {
return;
@@ -867,15 +883,6 @@
mDisplaySize.y - insets.bottom);
}
- private boolean isTrackpadDevice(int deviceId) {
- InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
- if (inputDevice == null) {
- return false;
- }
- return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE
- | InputDevice.SOURCE_TOUCHPAD);
- }
-
private boolean desktopExcludeRegionContains(int x, int y) {
return mDesktopModeExcludeRegion.contains(x, y);
}
@@ -1175,6 +1182,10 @@
// TODO(b/332635834): Disable this logging once b/332635834 is fixed.
Log.i(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
+ " lastReportedConfig=" + mLastReportedConfig);
+ final int diff = newConfig.diff(mLastReportedConfig);
+ if ((diff & CONFIG_FONT_SCALE) != 0 || (diff & ActivityInfo.CONFIG_DENSITY) != 0) {
+ updateCurrentUserResources();
+ }
mLastReportedConfig.updateFrom(newConfig);
updateDisplaySize();
}
@@ -1251,7 +1262,8 @@
pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog));
pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
- pw.println(" mIsTrackpadConnected=" + mIsTrackpadConnected);
+ pw.println(" mTrackpadsConnected=" + mTrackpadsConnected.stream().map(
+ String::valueOf).collect(joining()));
pw.println(" mUsingThreeButtonNav=" + mUsingThreeButtonNav);
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
if (mEdgeBackPlugin != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index d161c6b..7b67993 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -28,6 +28,7 @@
import com.android.systemui.qs.panels.domain.interactor.NoopGridConsistencyInteractor
import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
import com.android.systemui.qs.panels.shared.model.StretchedGridLayoutType
@@ -35,6 +36,12 @@
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
import com.android.systemui.qs.panels.ui.compose.StretchedGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModelImpl
+import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl
+import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModelImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -53,6 +60,15 @@
impl: NoopGridConsistencyInteractor
): GridTypeConsistencyInteractor
+ @Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
+
+ @Binds fun bindGridSizeViewModel(impl: InfiniteGridSizeViewModelImpl): InfiniteGridSizeViewModel
+
+ @Binds
+ fun bindIconLabelVisibilityViewModel(
+ impl: IconLabelVisibilityViewModelImpl
+ ): IconLabelVisibilityViewModel
+
@Binds @Named("Default") fun bindDefaultGridLayout(impl: PartitionedGridLayout): GridLayout
companion object {
@@ -64,6 +80,13 @@
}
@Provides
+ @SysUISingleton
+ @IconLabelVisibilityLog
+ fun providesIconTileLabelVisibilityLog(factory: LogBufferFactory): LogBuffer {
+ return factory.create("IconLabelVisibilityLog", 50)
+ }
+
+ @Provides
@IntoSet
fun provideGridLayout(gridLayout: InfiniteGridLayout): Pair<GridLayoutType, GridLayout> {
return Pair(InfiniteGridLayoutType, gridLayout)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt
new file mode 100644
index 0000000..686e5f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.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.qs.panels.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Repository for whether to show the labels of icon tiles. */
+@SysUISingleton
+class IconLabelVisibilityRepository @Inject constructor() {
+ // TODO(b/341735914): Persist and back up showLabels
+ private val _showLabels = MutableStateFlow(false)
+ val showLabels: StateFlow<Boolean> = _showLabels.asStateFlow()
+
+ fun setShowLabels(showLabels: Boolean) {
+ _showLabels.value = showLabels
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
new file mode 100644
index 0000000..a871531
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.qs.panels.data.repository.IconLabelVisibilityRepository
+import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class IconLabelVisibilityInteractor
+@Inject
+constructor(
+ private val repo: IconLabelVisibilityRepository,
+ @IconLabelVisibilityLog private val logBuffer: LogBuffer,
+ @Application scope: CoroutineScope,
+) {
+ val showLabels: StateFlow<Boolean> =
+ repo.showLabels
+ .onEach { logChange(it) }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), repo.showLabels.value)
+
+ fun setShowLabels(showLabels: Boolean) {
+ repo.setShowLabels(showLabels)
+ }
+
+ private fun logChange(showLabels: Boolean) {
+ logBuffer.log(
+ LOG_BUFFER_ICON_TILE_LABEL_VISIBILITY_CHANGE_TAG,
+ LogLevel.DEBUG,
+ { bool1 = showLabels },
+ { "Icon tile label visibility changed: $bool1" }
+ )
+ }
+
+ private companion object {
+ const val LOG_BUFFER_ICON_TILE_LABEL_VISIBILITY_CHANGE_TAG = "IconLabelVisibilityChange"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/IconLabelVisibilityLog.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/IconLabelVisibilityLog.kt
new file mode 100644
index 0000000..c92234c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/IconLabelVisibilityLog.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.qs.panels.shared.model
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class IconLabelVisibilityLog()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index f5ee720..4aeaa7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -26,9 +26,9 @@
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
-import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
@@ -38,8 +38,8 @@
class InfiniteGridLayout
@Inject
constructor(
- private val iconTilesInteractor: IconTilesInteractor,
- private val gridSizeInteractor: InfiniteGridSizeInteractor
+ private val iconTilesViewModel: IconTilesViewModel,
+ private val gridSizeViewModel: InfiniteGridSizeViewModel,
) : GridLayout {
@Composable
@@ -52,8 +52,8 @@
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
- val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+ val iconTilesSpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
items(
@@ -68,9 +68,9 @@
}
) { index ->
Tile(
- tiles[index],
- iconTilesSpecs.contains(tiles[index].spec),
- Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ tile = tiles[index],
+ iconOnly = iconTilesSpecs.contains(tiles[index].spec),
+ modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
)
}
}
@@ -83,8 +83,8 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
) {
- val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
- val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+ val iconOnlySpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
DefaultEditTileGrid(
tiles = tiles,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index 8d0b386..708ef0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -32,6 +33,8 @@
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
@@ -39,14 +42,14 @@
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.modifiers.background
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
-import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.PartitionedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -54,12 +57,8 @@
import javax.inject.Inject
@SysUISingleton
-class PartitionedGridLayout
-@Inject
-constructor(
- private val iconTilesInteractor: IconTilesInteractor,
- private val gridSizeInteractor: InfiniteGridSizeInteractor,
-) : GridLayout {
+class PartitionedGridLayout @Inject constructor(private val viewModel: PartitionedGridViewModel) :
+ GridLayout {
@Composable
override fun TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
DisposableEffect(tiles) {
@@ -67,9 +66,11 @@
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
- val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
- val tileHeight = dimensionResource(id = R.dimen.qs_tile_height)
+ val iconTilesSpecs by viewModel.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by viewModel.columns.collectAsStateWithLifecycle()
+ val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
+ val largeTileHeight = tileHeight()
+ val iconTileHeight = tileHeight(showLabels)
val (smallTiles, largeTiles) = tiles.partition { iconTilesSpecs.contains(it.spec) }
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
@@ -78,7 +79,7 @@
Tile(
tile = largeTiles[index],
iconOnly = false,
- modifier = Modifier.height(tileHeight)
+ modifier = Modifier.height(largeTileHeight)
)
}
fillUpRow(nTiles = largeTiles.size, columns = columns / 2)
@@ -88,7 +89,8 @@
Tile(
tile = smallTiles[index],
iconOnly = true,
- modifier = Modifier.height(tileHeight)
+ showLabels = showLabels,
+ modifier = Modifier.height(iconTileHeight)
)
}
}
@@ -101,8 +103,9 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit
) {
- val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
- val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+ val iconOnlySpecs by viewModel.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by viewModel.columns.collectAsStateWithLifecycle()
+ val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
@@ -110,27 +113,56 @@
}
val isIconOnly: (TileSpec) -> Boolean =
remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
- val tileHeight = dimensionResource(id = R.dimen.qs_tile_height)
+ val largeTileHeight = tileHeight()
+ val iconTileHeight = tileHeight(showLabels)
val tilePadding = dimensionResource(R.dimen.qs_tile_margin_vertical)
Column(
verticalArrangement = Arrangement.spacedBy(tilePadding),
modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState())
) {
+ Row(
+ modifier =
+ Modifier.background(
+ color = MaterialTheme.colorScheme.surfaceVariant,
+ alpha = { 1f },
+ shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+ )
+ .padding(tilePadding)
+ ) {
+ Column(Modifier.padding(start = tilePadding)) {
+ Text(
+ text = "Show text labels",
+ color = MaterialTheme.colorScheme.onBackground,
+ fontWeight = FontWeight.Bold
+ )
+ Text(
+ text = "Display names under each tile",
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ Spacer(modifier = Modifier.weight(1f))
+ Switch(checked = showLabels, onCheckedChange = { viewModel.setShowLabels(it) })
+ }
+
CurrentTiles(
tiles = currentTiles,
- tileHeight = tileHeight,
+ largeTileHeight = largeTileHeight,
+ iconTileHeight = iconTileHeight,
tilePadding = tilePadding,
onRemoveTile = onRemoveTile,
isIconOnly = isIconOnly,
columns = columns,
+ showLabels = showLabels,
)
AvailableTiles(
tiles = otherTiles,
- tileHeight = tileHeight,
+ largeTileHeight = largeTileHeight,
+ iconTileHeight = iconTileHeight,
tilePadding = tilePadding,
addTileToEnd = addTileToEnd,
isIconOnly = isIconOnly,
+ showLabels = showLabels,
columns = columns,
)
}
@@ -139,23 +171,31 @@
@Composable
private fun CurrentTiles(
tiles: List<EditTileViewModel>,
- tileHeight: Dp,
+ largeTileHeight: Dp,
+ iconTileHeight: Dp,
tilePadding: Dp,
onRemoveTile: (TileSpec) -> Unit,
isIconOnly: (TileSpec) -> Boolean,
+ showLabels: Boolean,
columns: Int,
) {
val (smallTiles, largeTiles) = tiles.partition { isIconOnly(it.tileSpec) }
- val largeGridHeight = gridHeight(largeTiles.size, tileHeight, columns / 2, tilePadding)
- val smallGridHeight = gridHeight(smallTiles.size, tileHeight, columns, tilePadding)
+ val largeGridHeight = gridHeight(largeTiles.size, largeTileHeight, columns / 2, tilePadding)
+ val smallGridHeight = gridHeight(smallTiles.size, iconTileHeight, columns, tilePadding)
CurrentTilesContainer {
TileLazyGrid(
columns = GridCells.Fixed(columns),
modifier = Modifier.height(largeGridHeight),
) {
- editTiles(largeTiles, ClickAction.REMOVE, onRemoveTile, { false }, true)
+ editTiles(
+ largeTiles,
+ ClickAction.REMOVE,
+ onRemoveTile,
+ { false },
+ indicatePosition = true
+ )
}
}
CurrentTilesContainer {
@@ -163,7 +203,14 @@
columns = GridCells.Fixed(columns),
modifier = Modifier.height(smallGridHeight),
) {
- editTiles(smallTiles, ClickAction.REMOVE, onRemoveTile, { true }, true)
+ editTiles(
+ smallTiles,
+ ClickAction.REMOVE,
+ onRemoveTile,
+ { true },
+ showLabels = showLabels,
+ indicatePosition = true
+ )
}
}
}
@@ -171,19 +218,21 @@
@Composable
private fun AvailableTiles(
tiles: List<EditTileViewModel>,
- tileHeight: Dp,
+ largeTileHeight: Dp,
+ iconTileHeight: Dp,
tilePadding: Dp,
addTileToEnd: (TileSpec) -> Unit,
isIconOnly: (TileSpec) -> Boolean,
+ showLabels: Boolean,
columns: Int,
) {
val (tilesStock, tilesCustom) = tiles.partition { it.appName == null }
val (smallTiles, largeTiles) = tilesStock.partition { isIconOnly(it.tileSpec) }
- val largeGridHeight = gridHeight(largeTiles.size, tileHeight, columns / 2, tilePadding)
- val smallGridHeight = gridHeight(smallTiles.size, tileHeight, columns, tilePadding)
+ val largeGridHeight = gridHeight(largeTiles.size, largeTileHeight, columns / 2, tilePadding)
+ val smallGridHeight = gridHeight(smallTiles.size, iconTileHeight, columns, tilePadding)
val largeGridHeightCustom =
- gridHeight(tilesCustom.size, tileHeight, columns / 2, tilePadding)
+ gridHeight(tilesCustom.size, largeTileHeight, columns / 2, tilePadding)
// Add up the height of all three grids and add padding in between
val gridHeight =
@@ -199,7 +248,13 @@
fillUpRow(nTiles = largeTiles.size, columns = columns / 2)
// Small tiles
- editTiles(smallTiles, ClickAction.ADD, addTileToEnd, isIconOnly)
+ editTiles(
+ smallTiles,
+ ClickAction.ADD,
+ addTileToEnd,
+ isIconOnly,
+ showLabels = showLabels
+ )
fillUpRow(nTiles = smallTiles.size, columns = columns)
// Custom tiles, all large
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
index ddd97c2..70d629f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
@@ -27,11 +27,11 @@
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
-import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
@@ -41,8 +41,8 @@
class StretchedGridLayout
@Inject
constructor(
- private val iconTilesInteractor: IconTilesInteractor,
- private val gridSizeInteractor: InfiniteGridSizeInteractor,
+ private val iconTilesViewModel: IconTilesViewModel,
+ private val gridSizeViewModel: InfiniteGridSizeViewModel,
) : GridLayout {
@Composable
@@ -60,7 +60,7 @@
// Icon [3 | 4]
// Large [6 | 8]
val columns = 12
- val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+ val iconTilesSpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
val stretchedTiles =
remember(tiles) {
val sizedTiles =
@@ -80,9 +80,9 @@
TileLazyGrid(columns = GridCells.Fixed(columns), modifier = modifier) {
items(stretchedTiles.size, span = { GridItemSpan(stretchedTiles[it].width) }) { index ->
Tile(
- stretchedTiles[index].tile,
- iconTilesSpecs.contains(stretchedTiles[index].tile.spec),
- Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ tile = stretchedTiles[index].tile,
+ iconOnly = iconTilesSpecs.contains(stretchedTiles[index].tile.spec),
+ modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
)
}
}
@@ -95,8 +95,8 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit
) {
- val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
- val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+ val iconOnlySpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
DefaultEditTileGrid(
tiles = tiles,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index e8c65a5..a6838c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -69,6 +69,9 @@
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
@@ -98,6 +101,7 @@
fun Tile(
tile: TileViewModel,
iconOnly: Boolean,
+ showLabels: Boolean = false,
modifier: Modifier,
) {
val state: TileUiState by
@@ -136,7 +140,8 @@
secondaryLabel = state.secondaryLabel.toString(),
icon = icon,
colors = state.colors,
- iconOnly = iconOnly
+ iconOnly = iconOnly,
+ showLabels = showLabels,
)
}
}
@@ -213,6 +218,7 @@
clickAction: ClickAction,
onClick: (TileSpec) -> Unit,
isIconOnly: (TileSpec) -> Boolean,
+ showLabels: Boolean = false,
indicatePosition: Boolean = false,
) {
items(
@@ -250,10 +256,13 @@
this.stateDescription = stateDescription
}
) {
+ val iconOnly = isIconOnly(viewModel.tileSpec)
+ val tileHeight = tileHeight(iconOnly && showLabels)
EditTile(
tileViewModel = viewModel,
- isIconOnly(viewModel.tileSpec),
- modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ iconOnly = iconOnly,
+ showLabels = showLabels,
+ modifier = Modifier.height(tileHeight)
)
if (canClick) {
Badge(clickAction, Modifier.align(Alignment.TopEnd))
@@ -281,6 +290,7 @@
fun EditTile(
tileViewModel: EditTileViewModel,
iconOnly: Boolean,
+ showLabels: Boolean,
modifier: Modifier = Modifier,
) {
val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
@@ -297,6 +307,7 @@
colors = colors,
icon = tileViewModel.icon,
iconOnly = iconOnly,
+ showLabels = showLabels,
animateIconToEnd = true,
)
}
@@ -380,9 +391,26 @@
icon: Icon,
colors: TileColorAttributes,
iconOnly: Boolean,
+ showLabels: Boolean = false,
animateIconToEnd: Boolean = false,
) {
- TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxHeight()
+ ) {
+ TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
+
+ if (iconOnly && showLabels) {
+ Text(
+ label,
+ maxLines = 2,
+ color = colorAttr(colors.label),
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
if (!iconOnly) {
Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
@@ -401,3 +429,16 @@
}
}
}
+
+@Composable
+fun tileHeight(iconWithLabel: Boolean = false): Dp {
+ return if (iconWithLabel) {
+ TileDimensions.IconTileWithLabelHeight
+ } else {
+ dimensionResource(id = R.dimen.qs_tile_height)
+ }
+}
+
+private object TileDimensions {
+ val IconTileWithLabelHeight = 100.dp
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
new file mode 100644
index 0000000..5d4b8f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.domain.interactor.IconLabelVisibilityInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+interface IconLabelVisibilityViewModel {
+ val showLabels: StateFlow<Boolean>
+
+ fun setShowLabels(showLabels: Boolean)
+}
+
+@SysUISingleton
+class IconLabelVisibilityViewModelImpl
+@Inject
+constructor(private val interactor: IconLabelVisibilityInteractor) : IconLabelVisibilityViewModel {
+ override val showLabels: StateFlow<Boolean> = interactor.showLabels
+
+ override fun setShowLabels(showLabels: Boolean) {
+ interactor.setShowLabels(showLabels)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
new file mode 100644
index 0000000..9ad00c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+interface IconTilesViewModel {
+ val iconTilesSpecs: StateFlow<Set<TileSpec>>
+}
+
+@SysUISingleton
+class IconTilesViewModelImpl @Inject constructor(interactor: IconTilesInteractor) :
+ IconTilesViewModel {
+ override val iconTilesSpecs = interactor.iconTilesSpecs
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt
new file mode 100644
index 0000000..a4ee58f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+interface InfiniteGridSizeViewModel {
+ val columns: StateFlow<Int>
+}
+
+@SysUISingleton
+class InfiniteGridSizeViewModelImpl @Inject constructor(interactor: InfiniteGridSizeInteractor) :
+ InfiniteGridSizeViewModel {
+ override val columns: StateFlow<Int> = interactor.columns
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
new file mode 100644
index 0000000..730cf63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class PartitionedGridViewModel
+@Inject
+constructor(
+ iconTilesViewModel: IconTilesViewModel,
+ gridSizeViewModel: InfiniteGridSizeViewModel,
+ iconLabelVisibilityViewModel: IconLabelVisibilityViewModel,
+) :
+ IconTilesViewModel by iconTilesViewModel,
+ InfiniteGridSizeViewModel by gridSizeViewModel,
+ IconLabelVisibilityViewModel by iconLabelVisibilityViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 56588ff..8887f58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -677,6 +677,10 @@
mId = id;
}
+ public int getResourceId() {
+ return mId;
+ }
+
@Override
public boolean equals(Object o) {
return o instanceof DrawableIconWithRes && ((DrawableIconWithRes) o).mId == mId;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
index c0fc52e..f088943 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
@@ -18,6 +18,7 @@
import android.content.res.Resources
import android.content.res.Resources.Theme
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
@@ -82,7 +83,8 @@
secondaryLabel = resources.getString(R.string.qs_alarm_tile_no_alarm)
}
}
-
+ iconRes = R.drawable.ic_alarm
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
sideViewIcon = QSTileState.SideViewIcon.Chevron
contentDescription = label
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
index 0c08fba..bcf0935 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
@@ -38,17 +38,10 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.battery_detail_switch_title)
contentDescription = label
-
- icon = {
- Icon.Loaded(
- resources.getDrawable(
- if (data.isPowerSaving) R.drawable.qs_battery_saver_icon_on
- else R.drawable.qs_battery_saver_icon_off,
- theme
- ),
- null
- )
- }
+ iconRes =
+ if (data.isPowerSaving) R.drawable.qs_battery_saver_icon_on
+ else R.drawable.qs_battery_saver_icon_off
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
sideViewIcon = QSTileState.SideViewIcon.None
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
index 1efbfd7..cad7c65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
@@ -37,6 +37,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction)
+ iconRes = R.drawable.ic_qs_color_correction
+
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index 58e7613..d7d6124 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -37,14 +37,16 @@
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
+ iconRes =
+ if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ }
val icon =
Icon.Loaded(
resources.getDrawable(
- if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- },
+ iconRes!!,
theme,
),
contentDescription = null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
index 26069c7..6b4dda1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
@@ -36,10 +36,11 @@
override fun map(config: QSTileConfig, data: FontScalingTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
+ iconRes = R.drawable.ic_qs_font_scaling
val icon =
Icon.Loaded(
resources.getDrawable(
- R.drawable.ic_qs_font_scaling,
+ iconRes!!,
theme,
),
contentDescription = null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index caae4d2..e543e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -53,6 +53,7 @@
stateDescription = data.stateDescription.loadContentDescription(context)
contentDescription = data.contentDescription.loadContentDescription(context)
+ iconRes = data.iconId
if (data.icon != null) {
this.icon = { data.icon }
} else if (data.iconId != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
index 4af9854..40aee65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
@@ -41,22 +41,13 @@
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
- icon = {
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_invert_colors_icon_on, theme),
- null
- )
- }
+ iconRes = R.drawable.qs_invert_colors_icon_on
} else {
activationState = QSTileState.ActivationState.INACTIVE
secondaryLabel = subtitleArray[1]
- icon = {
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_invert_colors_icon_off, theme),
- null
- )
- }
+ iconRes = R.drawable.qs_invert_colors_icon_off
}
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index fe5445d..d58f5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -37,14 +37,16 @@
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
+ iconRes =
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ }
val icon =
Icon.Loaded(
resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- },
+ iconRes!!,
theme,
),
contentDescription = null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
index 5c2dcfc..bcf7cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
@@ -52,21 +52,14 @@
if (data.isActivated) {
activationState = QSTileState.ActivationState.ACTIVE
- val loadedIcon =
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_nightlight_icon_on, theme),
- contentDescription = null
- )
- icon = { loadedIcon }
+ iconRes = R.drawable.qs_nightlight_icon_on
} else {
activationState = QSTileState.ActivationState.INACTIVE
- val loadedIcon =
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_nightlight_icon_off, theme),
- contentDescription = null
- )
- icon = { loadedIcon }
+ iconRes = R.drawable.qs_nightlight_icon_off
}
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = { loadedIcon }
secondaryLabel = getSecondaryLabel(data, resources)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
index 9166ed8..4080996 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
@@ -38,15 +38,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
val subtitleArray = resources.getStringArray(R.array.tile_states_onehanded)
label = resources.getString(R.string.quick_settings_onehanded_label)
- icon = {
- Icon.Loaded(
- resources.getDrawable(
- com.android.internal.R.drawable.ic_qs_one_handed_mode,
- theme
- ),
- null
- )
- }
+ iconRes = com.android.internal.R.drawable.ic_qs_one_handed_mode
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
secondaryLabel = subtitleArray[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
index 45a7717..8231742 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -38,9 +38,8 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = resources.getString(R.string.qr_code_scanner_title)
contentDescription = label
- icon = {
- Icon.Loaded(resources.getDrawable(R.drawable.ic_qr_code_scanner, theme), null)
- }
+ iconRes = R.drawable.ic_qr_code_scanner
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
sideViewIcon = QSTileState.SideViewIcon.Chevron
supportedActions = setOf(QSTileState.UserAction.CLICK)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
index fca93df..85ee022 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
@@ -39,28 +39,23 @@
QSTileState.build(resources, theme, config.uiConfig) {
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- icon = {
- Icon.Loaded(
- drawable = resources.getDrawable(R.drawable.qs_extra_dim_icon_on, theme),
- contentDescription = null
- )
- }
-
+ iconRes = R.drawable.qs_extra_dim_icon_on
secondaryLabel =
resources
.getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_ACTIVE]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- icon = {
- Icon.Loaded(
- drawable = resources.getDrawable(R.drawable.qs_extra_dim_icon_off, theme),
- contentDescription = null
- )
- }
+ iconRes = R.drawable.qs_extra_dim_icon_off
secondaryLabel =
resources
.getStringArray(R.array.tile_states_reduce_brightness)[Tile.STATE_INACTIVE]
}
+ icon = {
+ Icon.Loaded(
+ drawable = resources.getDrawable(iconRes!!, theme),
+ contentDescription = null
+ )
+ }
label =
resources.getString(com.android.internal.R.string.reduce_bright_colors_feature_name)
contentDescription = label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index 070cdef..8e80fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -44,12 +44,7 @@
if (data.isRotationLocked) {
activationState = QSTileState.ActivationState.INACTIVE
this.secondaryLabel = EMPTY_SECONDARY_STRING
- this.icon = {
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_auto_rotate_icon_off, theme),
- contentDescription = null
- )
- }
+ iconRes = R.drawable.qs_auto_rotate_icon_off
} else {
activationState = QSTileState.ActivationState.ACTIVE
this.secondaryLabel =
@@ -58,12 +53,10 @@
} else {
EMPTY_SECONDARY_STRING
}
- this.icon = {
- Icon.Loaded(
- resources.getDrawable(R.drawable.qs_auto_rotate_icon_on, theme),
- contentDescription = null
- )
- }
+ this.iconRes = R.drawable.qs_auto_rotate_icon_on
+ }
+ this.icon = {
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
}
if (isDeviceFoldable()) {
this.secondaryLabel = getSecondaryLabelWithPosture(this.activationState)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
index df25600..888bba87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -36,7 +36,6 @@
override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
with(data) {
- val iconRes: Int
if (isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
iconRes = R.drawable.qs_data_saver_icon_on
@@ -47,7 +46,7 @@
secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
}
val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
icon = { loadedIcon }
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
index b58774b..7446708 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -42,9 +42,10 @@
when (data) {
is ScreenRecordModel.Recording -> {
activationState = QSTileState.ActivationState.ACTIVE
+ iconRes = R.drawable.qs_screen_record_icon_on
val loadedIcon =
Icon.Loaded(
- resources.getDrawable(R.drawable.qs_screen_record_icon_on, theme),
+ resources.getDrawable(iconRes!!, theme),
contentDescription = null
)
icon = { loadedIcon }
@@ -53,9 +54,10 @@
}
is ScreenRecordModel.Starting -> {
activationState = QSTileState.ActivationState.ACTIVE
+ iconRes = R.drawable.qs_screen_record_icon_on
val loadedIcon =
Icon.Loaded(
- resources.getDrawable(R.drawable.qs_screen_record_icon_on, theme),
+ resources.getDrawable(iconRes!!, theme),
contentDescription = null
)
icon = { loadedIcon }
@@ -65,9 +67,10 @@
}
is ScreenRecordModel.DoingNothing -> {
activationState = QSTileState.ActivationState.INACTIVE
+ iconRes = R.drawable.qs_screen_record_icon_off
val loadedIcon =
Icon.Loaded(
- resources.getDrawable(R.drawable.qs_screen_record_icon_off, theme),
+ resources.getDrawable(iconRes!!, theme),
contentDescription = null
)
icon = { loadedIcon }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
index 52622d2..597cf27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
@@ -50,15 +50,8 @@
contentDescription = label
supportedActions =
setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
- icon = {
- Icon.Loaded(
- resources.getDrawable(
- sensorPrivacyTileResources.getIconRes(data.isBlocked),
- theme
- ),
- null
- )
- }
+ iconRes = sensorPrivacyTileResources.getIconRes(data.isBlocked)
+ icon = { Icon.Loaded(resources.getDrawable(iconRes!!, theme), null) }
sideViewIcon = QSTileState.SideViewIcon.None
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
index ffef2b6..f29c745d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
@@ -117,12 +117,12 @@
}
}
- val iconRes =
+ iconRes =
if (activationState == QSTileState.ActivationState.ACTIVE)
R.drawable.qs_light_dark_theme_icon_on
else R.drawable.qs_light_dark_theme_icon_off
val loadedIcon =
- Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null)
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
icon = { loadedIcon }
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
index 55445bb..eee95b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
@@ -41,15 +41,9 @@
QSTileState.build(resources, theme, config.uiConfig) {
label = getTileLabel()!!
contentDescription = label
-
+ iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
icon = {
- Icon.Loaded(
- resources.getDrawable(
- com.android.internal.R.drawable.stat_sys_managed_profile_status,
- theme
- ),
- contentDescription = null
- )
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
}
when (data) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index b927e41..ae6c014 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -29,10 +29,14 @@
* [QSTileState.build] for better state creation experience and preset default values for certain
* fields.
*
+ * @param iconRes For when we want to have Loaded icon, but still keep a reference to the resource
+ * id. A use case would be for tests that have to compare animated drawables.
+ *
* // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
*/
data class QSTileState(
val icon: () -> Icon?,
+ val iconRes: Int?,
val label: CharSequence,
val activationState: ActivationState,
val secondaryLabel: CharSequence?,
@@ -111,6 +115,7 @@
var icon: () -> Icon?,
var label: CharSequence,
) {
+ var iconRes: Int? = null
var activationState: ActivationState = ActivationState.INACTIVE
var secondaryLabel: CharSequence? = null
var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
@@ -123,6 +128,7 @@
fun build(): QSTileState =
QSTileState(
icon,
+ iconRes,
label,
activationState,
secondaryLabel,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 5346b23..7be13e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -28,6 +28,7 @@
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -241,7 +242,9 @@
iconSupplier = Supplier {
when (val stateIcon = viewModelState.icon()) {
- is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
+ is Icon.Loaded ->
+ if (viewModelState.iconRes == null) DrawableIcon(stateIcon.drawable)
+ else DrawableIconWithRes(stateIcon.drawable, viewModelState.iconRes)
is Icon.Resource -> ResourceIcon.get(stateIcon.res)
null -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 4ab0918..9e62280 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -90,12 +90,18 @@
)
}
systemUiProxy.dismissKeyguard()
- transitionCoordinator?.startExit()
+ var transitionOptions: ActivityOptions? = null
+ if (transitionCoordinator?.decor?.isAttachedToWindow == true) {
+ transitionCoordinator.startExit()
+ transitionOptions = options
+ }
if (user == myUserHandle()) {
- withContext(mainDispatcher) { context.startActivity(intent, options?.toBundle()) }
+ withContext(mainDispatcher) {
+ context.startActivity(intent, transitionOptions?.toBundle())
+ }
} else {
- launchCrossProfileIntent(user, intent, options?.toBundle())
+ launchCrossProfileIntent(user, intent, transitionOptions?.toBundle())
}
if (overrideTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
new file mode 100644
index 0000000..2ffb783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.assist.AssistContent
+import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.UUID
+
+/**
+ * Responsible for obtaining the actions for each screenshot and sending them to the view model.
+ * Ensures that only actions from screenshots that are currently being shown are added to the view
+ * model.
+ */
+class ScreenshotActionsController
+@AssistedInject
+constructor(
+ private val viewModel: ScreenshotViewModel,
+ private val actionsProviderFactory: ScreenshotActionsProvider.Factory,
+ @Assisted val actionExecutor: ActionExecutor
+) {
+ private val actionProviders: MutableMap<UUID, ScreenshotActionsProvider> = mutableMapOf()
+ private var currentScreenshotId: UUID? = null
+
+ fun setCurrentScreenshot(screenshot: ScreenshotData): UUID {
+ val screenshotId = UUID.randomUUID()
+ currentScreenshotId = screenshotId
+ actionProviders[screenshotId] =
+ actionsProviderFactory.create(
+ screenshotId,
+ screenshot,
+ actionExecutor,
+ ActionsCallback(screenshotId),
+ )
+ return screenshotId
+ }
+
+ fun endScreenshotSession() {
+ currentScreenshotId = null
+ }
+
+ fun onAssistContent(screenshotId: UUID, assistContent: AssistContent?) {
+ actionProviders[screenshotId]?.onAssistContent(assistContent)
+ }
+
+ fun onScrollChipReady(screenshotId: UUID, onClick: Runnable) {
+ if (screenshotId == currentScreenshotId) {
+ actionProviders[screenshotId]?.onScrollChipReady(onClick)
+ }
+ }
+
+ fun onScrollChipInvalidated() {
+ for (provider in actionProviders.values) {
+ provider.onScrollChipInvalidated()
+ }
+ }
+
+ fun setCompletedScreenshot(screenshotId: UUID, result: ScreenshotSavedResult) {
+ if (screenshotId == currentScreenshotId) {
+ actionProviders[screenshotId]?.setCompletedScreenshot(result)
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun getController(actionExecutor: ActionExecutor): ScreenshotActionsController
+ }
+
+ inner class ActionsCallback(private val screenshotId: UUID) {
+ fun providePreviewAction(onClick: () -> Unit) {
+ if (screenshotId == currentScreenshotId) {
+ viewModel.setPreviewAction(onClick)
+ }
+ }
+
+ fun provideActionButton(
+ appearance: ActionButtonAppearance,
+ showDuringEntrance: Boolean,
+ onClick: () -> Unit
+ ): Int {
+ if (screenshotId == currentScreenshotId) {
+ return viewModel.addAction(appearance, showDuringEntrance, onClick)
+ }
+ return 0
+ }
+
+ fun updateActionButtonAppearance(buttonId: Int, appearance: ActionButtonAppearance) {
+ if (screenshotId == currentScreenshotId) {
+ viewModel.updateActionAppearance(buttonId, appearance)
+ }
+ }
+
+ fun updateActionButtonVisibility(buttonId: Int, visible: Boolean) {
+ if (screenshotId == currentScreenshotId) {
+ viewModel.setActionVisibility(buttonId, visible)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index a1dd415..b8029c8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -29,10 +29,10 @@
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
-import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.util.UUID
/**
* Provides actions for screenshots. This class can be overridden by a vendor-specific SysUI
@@ -51,9 +51,10 @@
interface Factory {
fun create(
+ requestId: UUID,
request: ScreenshotData,
- requestId: String,
actionExecutor: ActionExecutor,
+ actionsCallback: ScreenshotActionsController.ActionsCallback,
): ScreenshotActionsProvider
}
}
@@ -62,11 +63,11 @@
@AssistedInject
constructor(
private val context: Context,
- private val viewModel: ScreenshotViewModel,
private val uiEventLogger: UiEventLogger,
+ @Assisted val requestId: UUID,
@Assisted val request: ScreenshotData,
- @Assisted val requestId: String,
@Assisted val actionExecutor: ActionExecutor,
+ @Assisted val actionsCallback: ScreenshotActionsController.ActionsCallback,
) : ScreenshotActionsProvider {
private var addedScrollChip = false
private var onScrollClick: Runnable? = null
@@ -74,7 +75,7 @@
private var result: ScreenshotSavedResult? = null
init {
- viewModel.setPreviewAction {
+ actionsCallback.providePreviewAction {
debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
onDeferrableActionTapped { result ->
@@ -85,26 +86,8 @@
)
}
}
- viewModel.addAction(
- ActionButtonAppearance(
- AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_edit),
- context.resources.getString(R.string.screenshot_edit_label),
- context.resources.getString(R.string.screenshot_edit_description),
- ),
- showDuringEntrance = true,
- ) {
- debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" }
- uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString)
- onDeferrableActionTapped { result ->
- actionExecutor.startSharedTransition(
- createEdit(result.uri, context),
- result.user,
- true
- )
- }
- }
- viewModel.addAction(
+ actionsCallback.provideActionButton(
ActionButtonAppearance(
AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_share),
context.resources.getString(R.string.screenshot_share_label),
@@ -122,12 +105,31 @@
)
}
}
+
+ actionsCallback.provideActionButton(
+ ActionButtonAppearance(
+ AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_edit),
+ context.resources.getString(R.string.screenshot_edit_label),
+ context.resources.getString(R.string.screenshot_edit_description),
+ ),
+ showDuringEntrance = true,
+ ) {
+ debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" }
+ uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString)
+ onDeferrableActionTapped { result ->
+ actionExecutor.startSharedTransition(
+ createEdit(result.uri, context),
+ result.user,
+ true
+ )
+ }
+ }
}
override fun onScrollChipReady(onClick: Runnable) {
onScrollClick = onClick
if (!addedScrollChip) {
- viewModel.addAction(
+ actionsCallback.provideActionButton(
ActionButtonAppearance(
AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll),
context.resources.getString(R.string.screenshot_scroll_label),
@@ -161,9 +163,10 @@
@AssistedFactory
interface Factory : ScreenshotActionsProvider.Factory {
override fun create(
+ requestId: UUID,
request: ScreenshotData,
- requestId: String,
actionExecutor: ActionExecutor,
+ actionsCallback: ScreenshotActionsController.ActionsCallback,
): DefaultScreenshotActionsProvider
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 4c60090..e8dfac8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -192,7 +192,6 @@
private final WindowContext mContext;
private final FeatureFlags mFlags;
private final ScreenshotViewProxy mViewProxy;
- private final ScreenshotActionsProvider.Factory mActionsProviderFactory;
private final ScreenshotNotificationsController mNotificationsController;
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
@@ -202,7 +201,7 @@
private final ExecutorService mBgExecutor;
private final BroadcastSender mBroadcastSender;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final ActionExecutor mActionExecutor;
+ private final ScreenshotActionsController mActionsController;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
@@ -217,16 +216,18 @@
private final ActionIntentExecutor mActionIntentExecutor;
private final UserManager mUserManager;
private final AssistContentRequester mAssistContentRequester;
+ private final ActionExecutor mActionExecutor;
+
private final MessageContainerController mMessageContainerController;
private final AnnouncementResolver mAnnouncementResolver;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
private boolean mScreenshotTakenInPortrait;
- private boolean mBlockAttach;
+ private boolean mAttachRequested;
+ private boolean mDetachRequested;
private Animator mScreenshotAnimation;
private RequestCallback mCurrentRequestCallback;
- private ScreenshotActionsProvider mActionsProvider;
private String mPackageName = "";
private final BroadcastReceiver mCopyBroadcastReceiver;
@@ -253,7 +254,6 @@
WindowManager windowManager,
FeatureFlags flags,
ScreenshotViewProxy.Factory viewProxyFactory,
- ScreenshotActionsProvider.Factory actionsProviderFactory,
ScreenshotSmartActions screenshotSmartActions,
ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
UiEventLogger uiEventLogger,
@@ -265,6 +265,7 @@
BroadcastSender broadcastSender,
BroadcastDispatcher broadcastDispatcher,
ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
+ ScreenshotActionsController.Factory screenshotActionsControllerFactory,
ActionIntentExecutor actionIntentExecutor,
ActionExecutor.Factory actionExecutorFactory,
UserManager userManager,
@@ -276,7 +277,6 @@
@Assisted boolean showUIOnExternalDisplay
) {
mScreenshotSmartActions = screenshotSmartActions;
- mActionsProviderFactory = actionsProviderFactory;
mNotificationsController = screenshotNotificationsControllerFactory.create(
display.getDisplayId());
mUiEventLogger = uiEventLogger;
@@ -327,6 +327,8 @@
finishDismiss();
return Unit.INSTANCE;
});
+ mActionsController = screenshotActionsControllerFactory.getController(mActionExecutor);
+
// Sound is only reproduced from the controller of the default display.
if (mDisplay.getDisplayId() == Display.DEFAULT_DISPLAY) {
@@ -403,20 +405,21 @@
return;
}
+ final UUID requestId;
if (screenshotShelfUi2()) {
- final UUID requestId = UUID.randomUUID();
- final String screenshotId = String.format("Screenshot_%s", requestId);
- mActionsProvider = mActionsProviderFactory.create(
- screenshot, screenshotId, mActionExecutor);
+ requestId = mActionsController.setCurrentScreenshot(screenshot);
saveScreenshotInBackground(screenshot, requestId, finisher);
if (screenshot.getTaskId() >= 0) {
- mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
- assistContent -> mActionsProvider.onAssistContent(assistContent));
+ mAssistContentRequester.requestAssistContent(
+ screenshot.getTaskId(),
+ assistContent ->
+ mActionsController.onAssistContent(requestId, assistContent));
} else {
- mActionsProvider.onAssistContent(null);
+ mActionsController.onAssistContent(requestId, null);
}
} else {
+ requestId = UUID.randomUUID(); // passed through but unused for legacy UI
saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
this::showUiOnActionsReady, this::showUiOnQuickShareActionReady);
}
@@ -425,7 +428,7 @@
setWindowFocusable(true);
mViewProxy.requestFocus();
- enqueueScrollCaptureRequest(screenshot.getUserHandle());
+ enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
attachWindow();
@@ -586,11 +589,11 @@
mWindow.setContentView(mViewProxy.getView());
}
- private void enqueueScrollCaptureRequest(UserHandle owner) {
+ private void enqueueScrollCaptureRequest(UUID requestId, UserHandle owner) {
// Wait until this window is attached to request because it is
// the reference used to locate the target window (below).
withWindowAttached(() -> {
- requestScrollCapture(owner);
+ requestScrollCapture(requestId, owner);
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
new ViewRootImpl.ActivityConfigCallback() {
@Override
@@ -600,14 +603,14 @@
// Hide the scroll chip until we know it's available in this
// orientation
if (screenshotShelfUi2()) {
- mActionsProvider.onScrollChipInvalidated();
+ mActionsController.onScrollChipInvalidated();
} else {
mViewProxy.hideScrollChip();
}
// Delay scroll capture eval a bit to allow the underlying activity
// to set up in the new orientation.
mScreenshotHandler.postDelayed(
- () -> requestScrollCapture(owner), 150);
+ () -> requestScrollCapture(requestId, owner), 150);
mViewProxy.updateInsets(
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
// Screenshot animation calculations won't be valid anymore,
@@ -629,7 +632,7 @@
});
}
- private void requestScrollCapture(UserHandle owner) {
+ private void requestScrollCapture(UUID requestId, UserHandle owner) {
mScrollCaptureExecutor.requestScrollCapture(
mDisplay.getDisplayId(),
mWindow.getDecorView().getWindowToken(),
@@ -637,10 +640,8 @@
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
0, response.getPackageName());
if (screenshotShelfUi2()) {
- if (mActionsProvider != null) {
- mActionsProvider.onScrollChipReady(
- () -> onScrollButtonClicked(owner, response));
- }
+ mActionsController.onScrollChipReady(requestId,
+ () -> onScrollButtonClicked(owner, response));
} else {
mViewProxy.showScrollChip(response.getPackageName(),
() -> onScrollButtonClicked(owner, response));
@@ -672,7 +673,7 @@
() -> {
final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
owner, mContext);
- mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null);
+ mContext.startActivity(intent);
},
mViewProxy::restoreNonScrollingUi,
mViewProxy::startLongScreenshotTransition);
@@ -687,7 +688,7 @@
new ViewTreeObserver.OnWindowAttachListener() {
@Override
public void onWindowAttached() {
- mBlockAttach = false;
+ mAttachRequested = false;
decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
action.run();
}
@@ -703,13 +704,13 @@
@MainThread
private void attachWindow() {
View decorView = mWindow.getDecorView();
- if (decorView.isAttachedToWindow() || mBlockAttach) {
+ if (decorView.isAttachedToWindow() || mAttachRequested) {
return;
}
if (DEBUG_WINDOW) {
Log.d(TAG, "attachWindow");
}
- mBlockAttach = true;
+ mAttachRequested = true;
mWindowManager.addView(decorView, mWindowLayoutParams);
decorView.requestApplyInsets();
@@ -727,6 +728,11 @@
Log.d(TAG, "Removing screenshot window");
}
mWindowManager.removeViewImmediate(decorView);
+ mDetachRequested = false;
+ }
+ if (mAttachRequested && !mDetachRequested) {
+ mDetachRequested = true;
+ withWindowAttached(this::removeWindow);
}
mViewProxy.stopInputListening();
@@ -826,6 +832,7 @@
/** Reset screenshot view and then call onCompleteRunnable */
private void finishDismiss() {
Log.d(TAG, "finishDismiss");
+ mActionsController.endScreenshotSession();
mScrollCaptureExecutor.close();
if (mCurrentRequestCallback != null) {
mCurrentRequestCallback.onFinish();
@@ -846,9 +853,8 @@
ImageExporter.Result result = future.get();
Log.d(TAG, "Saved screenshot: " + result);
logScreenshotResultStatus(result.uri, screenshot.getUserHandle());
- mScreenshotHandler.resetTimeout();
if (result.uri != null) {
- mActionsProvider.setCompletedScreenshot(new ScreenshotSavedResult(
+ mActionsController.setCompletedScreenshot(requestId, new ScreenshotSavedResult(
result.uri, screenshot.getUserOrDefault(), result.timestamp));
}
if (DEBUG_CALLBACK) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 3ac070a..1b5fa34 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -22,8 +22,13 @@
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
+import android.graphics.Region
+import android.os.Looper
+import android.view.Choreographer
+import android.view.InputEvent
import android.view.KeyEvent
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.ScrollCaptureResponse
import android.view.View
import android.view.ViewTreeObserver
@@ -48,6 +53,8 @@
import com.android.systemui.screenshot.ui.binder.ScreenshotShelfViewBinder
import com.android.systemui.screenshot.ui.viewmodel.AnimationState
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
+import com.android.systemui.shared.system.InputChannelCompat
+import com.android.systemui.shared.system.InputMonitorCompat
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -91,6 +98,8 @@
override var isPendingSharedTransition = false
private val animationController = ScreenshotAnimationController(view, viewModel)
+ private var inputMonitor: InputMonitorCompat? = null
+ private var inputEventReceiver: InputChannelCompat.InputEventReceiver? = null
init {
shelfViewBinder.bind(
@@ -106,20 +115,25 @@
setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
debugLog(DEBUG_WINDOW) { "adding OnComputeInternalInsetsListener" }
view.viewTreeObserver.addOnComputeInternalInsetsListener { info ->
- val touchableRegion =
- view.getTouchRegion(
- windowManager.currentWindowMetrics.windowInsets.getInsets(
- WindowInsets.Type.systemGestures()
- )
- )
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION)
- info.touchableRegion.set(touchableRegion)
+ info.touchableRegion.set(getTouchRegion())
}
screenshotPreview = view.screenshotPreview
thumbnailObserver.setViews(
view.blurredScreenshotPreview,
view.requireViewById(R.id.screenshot_preview_border)
)
+ view.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ startInputListening()
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ stopInputListening()
+ }
+ }
+ )
}
override fun reset() {
@@ -236,7 +250,12 @@
callbacks?.onUserInteraction() // reset the timeout
}
- override fun stopInputListening() {}
+ override fun stopInputListening() {
+ inputMonitor?.dispose()
+ inputMonitor = null
+ inputEventReceiver?.dispose()
+ inputEventReceiver = null
+ }
override fun requestFocus() {
view.requestFocus()
@@ -303,6 +322,32 @@
)
}
+ private fun startInputListening() {
+ stopInputListening()
+ inputMonitor =
+ InputMonitorCompat("Screenshot", displayId).also {
+ inputEventReceiver =
+ it.getInputReceiver(Looper.getMainLooper(), Choreographer.getInstance()) {
+ ev: InputEvent? ->
+ if (
+ ev is MotionEvent &&
+ ev.actionMasked == MotionEvent.ACTION_DOWN &&
+ !getTouchRegion().contains(ev.rawX.toInt(), ev.rawY.toInt())
+ ) {
+ callbacks?.onTouchOutside()
+ }
+ }
+ }
+ }
+
+ private fun getTouchRegion(): Region {
+ return view.getTouchRegion(
+ windowManager.currentWindowMetrics.windowInsets.getInsets(
+ WindowInsets.Type.systemGestures()
+ )
+ )
+ }
+
@AssistedFactory
interface Factory : ScreenshotViewProxy.Factory {
override fun getProxy(context: Context, displayId: Int): ScreenshotShelfViewProxy
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index a0c9391..da2024b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -17,9 +17,12 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.carrier.ShadeCarrierGroupControllerLog
import com.android.systemui.shade.data.repository.PrivacyChipRepository
import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -143,6 +146,13 @@
fun providesQSContainerController(impl: QSSceneAdapterImpl): QSContainerController {
return impl
}
+
+ @Provides
+ @SysUISingleton
+ @ShadeCarrierGroupControllerLog
+ fun provideShadeCarrierLog(factory: LogBufferFactory): LogBuffer {
+ return factory.create("ShadeCarrierGroupControllerLog", 400)
+ }
}
@Binds
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 4b6dd8d..5e4b173 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -97,6 +97,8 @@
private final SlotIndexResolver mSlotIndexResolver;
+ private final ShadeCarrierGroupControllerLogger mLogger;
+
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
@@ -148,6 +150,7 @@
ActivityStarter activityStarter,
@Background Handler bgHandler,
@Main Looper mainLooper,
+ ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder,
Context context,
@@ -160,6 +163,7 @@
mContext = context;
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
+ mLogger = logger;
mNetworkController = networkController;
mStatusBarPipelineFlags = statusBarPipelineFlags;
mCarrierTextManager = carrierTextManagerBuilder
@@ -374,10 +378,13 @@
return;
}
+ mLogger.logHandleUpdateCarrierInfo(info);
+
mNoSimTextView.setVisibility(View.GONE);
if (!info.airplaneMode && info.anySimReady) {
boolean[] slotSeen = new boolean[SIM_SLOTS];
if (info.listOfCarriers.length == info.subscriptionIds.length) {
+ mLogger.logUsingSimViews();
for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
int slot = getSlotIndex(info.subscriptionIds[i]);
if (slot >= SIM_SLOTS) {
@@ -405,9 +412,11 @@
}
}
} else {
- Log.e(TAG, "Carrier information arrays not of same length");
+ mLogger.logInvalidArrayLengths(
+ info.listOfCarriers.length, info.subscriptionIds.length);
}
} else {
+ 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++) {
@@ -458,6 +467,7 @@
private final ActivityStarter mActivityStarter;
private final Handler mHandler;
private final Looper mLooper;
+ private final ShadeCarrierGroupControllerLogger mLogger;
private final NetworkController mNetworkController;
private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
private final Context mContext;
@@ -472,6 +482,7 @@
ActivityStarter activityStarter,
@Background Handler handler,
@Main Looper looper,
+ ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder,
Context context,
@@ -484,6 +495,7 @@
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
+ mLogger = logger;
mNetworkController = networkController;
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
mContext = context;
@@ -505,6 +517,7 @@
mActivityStarter,
mHandler,
mLooper,
+ mLogger,
mNetworkController,
mCarrierTextControllerBuilder,
mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLog.kt b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLog.kt
new file mode 100644
index 0000000..36aa7a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.carrier
+
+import javax.inject.Qualifier
+
+/** A [LogBuffer] for detailed carrier text logs for [ShadeCarrierGroupController]. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class ShadeCarrierGroupControllerLog
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
new file mode 100644
index 0000000..af06a35
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.carrier
+
+import com.android.keyguard.CarrierTextManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import javax.inject.Inject
+
+/** Logger for [ShadeCarrierGroupController], mostly to try and solve b/341841138. */
+@SysUISingleton
+class ShadeCarrierGroupControllerLogger
+@Inject
+constructor(@ShadeCarrierGroupControllerLog val buffer: LogBuffer) {
+ /** De-structures the info object so that we don't have to generate new strings */
+ fun logHandleUpdateCarrierInfo(info: CarrierTextManager.CarrierTextCallbackInfo) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = "${info.carrierText}"
+ bool1 = info.anySimReady
+ bool2 = info.airplaneMode
+ },
+ {
+ "handleUpdateCarrierInfo: " +
+ "result=(carrierText=$str1, anySimReady=$bool1, airplaneMode=$bool2)"
+ },
+ )
+ }
+
+ fun logInvalidArrayLengths(numCarriers: Int, numSubs: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.ERROR,
+ {
+ int1 = numCarriers
+ int2 = numSubs
+ },
+ { "â”— carriers.length != subIds.length. carriers.length=$int1 subs.length=$int2" },
+ )
+ }
+
+ fun logUsingNoSimView(text: CharSequence) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ { str1 = "$text" },
+ { "â”— updating No SIM view with text=$str1" },
+ )
+ }
+
+ fun logUsingSimViews() {
+ buffer.log(TAG, LogLevel.VERBOSE, {}, { "â”— updating SIM views" })
+ }
+
+ private companion object {
+ const val TAG = "SCGC"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FlingInfo.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FlingInfo.kt
index d7f96e6..ea2f9ab 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FlingInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FlingInfo.kt
@@ -16,11 +16,16 @@
package com.android.systemui.shade.data.repository
+import java.util.UUID
+
/**
* Information about a fling on the shade: whether we're flinging expanded or collapsed, and the
* velocity of the touch gesture that started the fling (if applicable).
*/
-data class FlingInfo(
+data class FlingInfo @JvmOverloads constructor(
val expand: Boolean,
val velocity: Float = 0f,
+
+ /** Required to emit duplicate FlingInfo from StateFlow. */
+ val id: UUID = UUID.randomUUID()
)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 53c10a3..fe16fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -49,11 +49,13 @@
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
shadeRepository: ShadeRepository,
) : BaseShadeInteractor {
+ override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
+
override val shadeExpansion: StateFlow<Float> =
- sceneBasedExpansion(sceneInteractor, Scenes.Shade)
+ sceneBasedExpansion(sceneInteractor, notificationsScene)
.stateIn(scope, SharingStarted.Eagerly, 0f)
- private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
+ private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene)
override val qsExpansion: StateFlow<Float> =
combine(
@@ -81,7 +83,7 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.toScene == Scenes.QuickSettings && state.fromScene != Scenes.Shade
+ state.toScene == quickSettingsScene && state.fromScene != notificationsScene
}
}
.distinctUntilChanged()
@@ -90,7 +92,7 @@
sceneInteractor.transitionState
.map { state ->
when (state) {
- is ObservableTransitionState.Idle -> state.currentScene == Scenes.QuickSettings
+ is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene
is ObservableTransitionState.Transition -> false
}
}
@@ -106,12 +108,10 @@
.stateIn(scope, SharingStarted.Eagerly, false)
override val isUserInteractingWithShade: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, Scenes.Shade)
+ sceneBasedInteracting(sceneInteractor, notificationsScene)
override val isUserInteractingWithQs: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, Scenes.QuickSettings)
-
- override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
+ sceneBasedInteracting(sceneInteractor, quickSettingsScene)
/**
* Returns a flow that uses scene transition progress to and from a scene that is pulled down
@@ -154,4 +154,20 @@
}
}
.distinctUntilChanged()
+
+ private val notificationsScene: SceneKey
+ get() =
+ if (shadeMode.value is ShadeMode.Dual) {
+ Scenes.NotificationsShade
+ } else {
+ Scenes.Shade
+ }
+
+ private val quickSettingsScene: SceneKey
+ get() =
+ if (shadeMode.value is ShadeMode.Dual) {
+ Scenes.QuickSettingsShade
+ } else {
+ Scenes.QuickSettings
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8d8a36a..47939ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -98,6 +98,8 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -183,11 +185,14 @@
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private KeyguardInteractor mKeyguardInteractor;
private final BiometricMessageInteractor mBiometricMessageInteractor;
+ private DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
+ private DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private String mPersistentUnlockMessage;
private String mAlignmentIndication;
private boolean mForceIsDismissible;
private CharSequence mTrustGrantedIndication;
private CharSequence mTransientIndication;
+ private CharSequence mTrustAgentErrorMessage;
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
private BiometricSourceType mBiometricMessageSource;
@@ -235,6 +240,13 @@
final Consumer<Set<Integer>> mCoExAcquisitionMsgIdsToShowCallback =
(Set<Integer> coExFaceAcquisitionMsgIdsToShow) -> mCoExFaceAcquisitionMsgIdsToShow =
coExFaceAcquisitionMsgIdsToShow;
+ @VisibleForTesting
+ final Consumer<Boolean> mIsFingerprintEngagedCallback =
+ (Boolean isEngaged) -> {
+ if (!isEngaged) {
+ showTrustAgentErrorMessage(mTrustAgentErrorMessage);
+ }
+ };
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
@@ -295,7 +307,9 @@
FeatureFlags flags,
IndicationHelper indicationHelper,
KeyguardInteractor keyguardInteractor,
- BiometricMessageInteractor biometricMessageInteractor
+ BiometricMessageInteractor biometricMessageInteractor,
+ DeviceEntryFingerprintAuthInteractor deviceEntryFingerprintAuthInteractor,
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor
) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
@@ -325,6 +339,8 @@
mIndicationHelper = indicationHelper;
mKeyguardInteractor = keyguardInteractor;
mBiometricMessageInteractor = biometricMessageInteractor;
+ mDeviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral.create();
@@ -409,6 +425,8 @@
collectFlow(mIndicationArea,
mBiometricMessageInteractor.getCoExFaceAcquisitionMsgIdsToShow(),
mCoExAcquisitionMsgIdsToShowCallback);
+ collectFlow(mIndicationArea, mDeviceEntryFingerprintAuthInteractor.isEngaged(),
+ mIsFingerprintEngagedCallback);
}
/**
@@ -944,19 +962,25 @@
if (!isSuccessMessage
&& mBiometricMessageSource == FINGERPRINT
- && biometricSourceType != FINGERPRINT) {
- // drop all non-fingerprint biometric messages if there's a fingerprint message showing
- mKeyguardLogger.logDropNonFingerprintMessage(
+ && biometricSourceType == FACE) {
+ // drop any face messages if there's a fingerprint message showing
+ mKeyguardLogger.logDropFaceMessage(
biometricMessage,
- biometricMessageFollowUp,
- biometricSourceType
+ biometricMessageFollowUp
);
return;
}
- mBiometricMessage = biometricMessage;
- mBiometricMessageFollowUp = biometricMessageFollowUp;
- mBiometricMessageSource = biometricSourceType;
+ if (mBiometricMessageSource != null && biometricSourceType == null) {
+ // If there's a current biometric message showing and a non-biometric message
+ // arrives, update the followup message with the non-biometric message.
+ // Keep the biometricMessage and biometricMessageSource the same.
+ mBiometricMessageFollowUp = biometricMessage;
+ } else {
+ mBiometricMessage = biometricMessage;
+ mBiometricMessageFollowUp = biometricMessageFollowUp;
+ mBiometricMessageSource = biometricSourceType;
+ }
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
hideBiometricMessageDelayed(
@@ -1455,7 +1479,7 @@
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showBiometricMessage(message, null);
+ showTrustAgentErrorMessage(message);
}
@Override
@@ -1467,6 +1491,10 @@
hideBiometricMessage();
mBiometricErrorMessageToShowOnScreenOn = null;
}
+
+ if (!running && biometricSourceType == FACE) {
+ showTrustAgentErrorMessage(mTrustAgentErrorMessage);
+ }
}
@Override
@@ -1533,6 +1561,25 @@
return getCurrentUser() == userId;
}
+ /**
+ * Only show trust agent messages after biometrics are no longer active.
+ */
+ private void showTrustAgentErrorMessage(CharSequence message) {
+ if (message == null) {
+ mTrustAgentErrorMessage = null;
+ return;
+ }
+ boolean fpEngaged = mDeviceEntryFingerprintAuthInteractor.isEngaged().getValue();
+ boolean faceRunning = mDeviceEntryFaceAuthInteractor.isRunning();
+ if (fpEngaged || faceRunning) {
+ mKeyguardLogger.delayShowingTrustAgentError(message, fpEngaged, faceRunning);
+ mTrustAgentErrorMessage = message;
+ } else {
+ mTrustAgentErrorMessage = null;
+ showBiometricMessage(message, null);
+ }
+ }
+
protected void showTrustGrantedMessage(boolean dismissKeyguard, @Nullable String message) {
mTrustGrantedIndication = message;
updateDeviceEntryIndication(false);
@@ -1639,6 +1686,7 @@
new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
+ mTrustAgentErrorMessage = null;
updateDeviceEntryIndication(false);
}
@@ -1649,6 +1697,7 @@
mKeyguardLogger.log(TAG, LogLevel.DEBUG, "clear messages");
mTopIndicationView.clearMessages();
mRotateTextViewController.clearMessages();
+ mTrustAgentErrorMessage = null;
} else {
updateDeviceEntryIndication(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index e85df0e..c57cf69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
index 70362c8..c3d37fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.chips.domain.interactor
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import kotlinx.coroutines.flow.StateFlow
/** Interface for an interactor that knows the state of a single type of ongoing activity chip. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt
index e63713b..c753918 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ui.model
+package com.android.systemui.statusbar.chips.domain.model
import android.view.View
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 6f16969..bff5686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,17 +16,51 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Interactor for the screen recording chip shown in the status bar. */
@SysUISingleton
-open class ScreenRecordChipInteractor @Inject constructor() : OngoingActivityChipInteractor {
- // TODO(b/332662551): Implement this flow.
+open class ScreenRecordChipInteractor
+@Inject
+constructor(
+ @Application scope: CoroutineScope,
+ screenRecordRepository: ScreenRecordRepository,
+ val systemClock: SystemClock,
+) : OngoingActivityChipInteractor {
override val chip: StateFlow<OngoingActivityChipModel> =
- MutableStateFlow(OngoingActivityChipModel.Hidden)
+ screenRecordRepository.screenRecordState
+ .map { state ->
+ when (state) {
+ is ScreenRecordModel.DoingNothing,
+ // TODO(b/332662551): Implement the 3-2-1 countdown chip.
+ is ScreenRecordModel.Starting -> OngoingActivityChipModel.Hidden
+ is ScreenRecordModel.Recording ->
+ OngoingActivityChipModel.Shown(
+ // TODO(b/332662551): Also provide a content description.
+ icon =
+ Icon.Resource(
+ R.drawable.stat_sys_screen_record,
+ contentDescription = null
+ ),
+ startTimeMs = systemClock.elapsedRealtime()
+ ) {
+ // TODO(b/332662551): Implement the pause dialog.
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 47b2b03..208eb50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -19,8 +19,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
index 8505c5f..0d5ade7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
@@ -98,10 +98,10 @@
return
}
val r = mContext.resources
- val defaultThreshold = r.getDimensionPixelSize(R.dimen.system_gestures_start_threshold)
- mSwipeStartThreshold[defaultThreshold, defaultThreshold, defaultThreshold] =
- defaultThreshold
- mSwipeDistanceThreshold = defaultThreshold
+ val startThreshold = r.getDimensionPixelSize(R.dimen.system_gestures_start_threshold)
+ mSwipeStartThreshold[startThreshold, startThreshold, startThreshold] = startThreshold
+ mSwipeDistanceThreshold =
+ r.getDimensionPixelSize(R.dimen.system_gestures_distance_threshold)
val display = DisplayManagerGlobal.getInstance().getRealDisplay(mContext.displayId)
val displayCutout = display.cutout
if (displayCutout != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
index 2fd0a53..0ece88d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
@@ -46,7 +46,7 @@
private var monitoringCurrentTouch: Boolean = false
private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
- com.android.internal.R.dimen.system_gestures_start_threshold
+ com.android.internal.R.dimen.system_gestures_distance_threshold
)
override fun onInputEvent(ev: InputEvent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 6137381..c2ce114 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -25,6 +26,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
@@ -34,6 +36,7 @@
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
@@ -73,16 +76,16 @@
}
is ObservableTransitionState.Transition -> {
if (
- (transitionState.fromScene == Scenes.Shade &&
- transitionState.toScene == Scenes.QuickSettings) ||
- (transitionState.fromScene == Scenes.QuickSettings &&
- transitionState.toScene == Scenes.Shade)
+ (transitionState.fromScene == notificationsScene &&
+ transitionState.toScene == quickSettingsScene) ||
+ (transitionState.fromScene == quickSettingsScene &&
+ transitionState.toScene == notificationsScene)
) {
1f
} else if (
(transitionState.fromScene == Scenes.Gone ||
transitionState.fromScene == Scenes.Lockscreen) &&
- transitionState.toScene == Scenes.QuickSettings
+ transitionState.toScene == quickSettingsScene
) {
// during QS expansion, increase fraction at same rate as scrim alpha,
// but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
@@ -152,7 +155,9 @@
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
- sceneInteractor.currentScene.map { it == Scenes.Shade }.dumpWhileCollecting("isScrollable")
+ sceneInteractor.currentScene
+ .map { it == notificationsScene }
+ .dumpWhileCollecting("isScrollable")
/** Whether the notification stack is displayed in doze mode. */
val isDozing: Flow<Boolean> by lazy {
@@ -162,4 +167,22 @@
keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
}
}
+
+ private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
+
+ private val notificationsScene: SceneKey
+ get() =
+ if (shadeMode.value is ShadeMode.Dual) {
+ Scenes.NotificationsShade
+ } else {
+ Scenes.Shade
+ }
+
+ private val quickSettingsScene: SceneKey
+ get() =
+ if (shadeMode.value is ShadeMode.Dual) {
+ Scenes.QuickSettingsShade
+ } else {
+ Scenes.QuickSettings
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 11feb97..f99a81e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -159,7 +159,7 @@
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (shouldBeVisible()) {
- updateTopEntry();
+ updateTopEntry("onLayoutChange");
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
@@ -220,7 +220,7 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- updateTopEntry();
+ updateTopEntry("onHeadsUpPinned");
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -231,7 +231,7 @@
mPhoneStatusBarTransitions.onHeadsUpStateChanged(isHeadsUp);
}
- private void updateTopEntry() {
+ private void updateTopEntry(String reason) {
NotificationEntry newEntry = null;
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
@@ -370,7 +370,7 @@
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- updateTopEntry();
+ updateTopEntry("onHeadsUpUnPinned");
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -388,7 +388,7 @@
updateHeadsUpHeaders();
}
if (isExpanded() != oldIsExpanded) {
- updateTopEntry();
+ updateTopEntry("setAppearFraction");
}
}
@@ -462,11 +462,11 @@
}
public void onStateChanged() {
- updateTopEntry();
+ updateTopEntry("onStateChanged");
}
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
- updateTopEntry();
+ updateTopEntry("onFullyHiddenChanged");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 68457ea..ffc859e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -249,7 +249,7 @@
for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
if (isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
- removeEntry(entry.getKey());
+ removeEntry(entry.getKey(), "onExpandingFinished");
}
}
}
@@ -381,7 +381,7 @@
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
if (isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
- removeEntry(entry.getKey());
+ removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
}
}
mEntriesToRemoveWhenReorderingAllowed.clear();
@@ -572,7 +572,7 @@
} else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
} else {
- removeEntry(entry.getKey());
+ removeEntry(entry.getKey(), "createRemoveRunnable");
}
};
}
@@ -661,7 +661,7 @@
}
}
for (String key : keysToRemove) {
- removeEntry(key);
+ removeEntry(key, "mStatusBarStateListener");
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 12f252d..9d9cc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.satellite.data.prod
import android.os.OutcomeReceiver
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
import android.telephony.satellite.NtnSignalStrengthCallback
import android.telephony.satellite.SatelliteManager
import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
@@ -38,6 +40,7 @@
import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Unknown
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
import java.util.Optional
import javax.inject.Inject
@@ -51,12 +54,15 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -92,13 +98,19 @@
@OptIn(ExperimentalCoroutinesApi::class)
companion object {
- /** Convenience function to switch to the supported flow */
+ /**
+ * Convenience function to switch to the supported flow. [retrySignal] is a flow that emits
+ * [Unit] whenever the [supported] flow needs to be restarted
+ */
fun <T> Flow<SatelliteSupport>.whenSupported(
supported: (SatelliteManager) -> Flow<T>,
orElse: Flow<T>,
- ): Flow<T> = flatMapLatest {
- when (it) {
- is Supported -> supported(it.satelliteManager)
+ retrySignal: Flow<Unit>,
+ ): Flow<T> = flatMapLatest { satelliteSupport ->
+ when (satelliteSupport) {
+ is Supported -> {
+ retrySignal.flatMapLatest { supported(satelliteSupport.satelliteManager) }
+ }
else -> orElse
}
}
@@ -132,6 +144,7 @@
@Inject
constructor(
satelliteManagerOpt: Optional<SatelliteManager>,
+ telephonyManager: TelephonyManager,
@Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
@OemSatelliteInputLog private val logBuffer: LogBuffer,
@@ -201,11 +214,65 @@
}
}
+ /**
+ * Note that we are given an "unbound" [TelephonyManager] (meaning it was not created with a
+ * specific `subscriptionId`). Therefore this is the radio power state of the
+ * DEFAULT_SUBSCRIPTION_ID subscription. This subscription, I am led to believe, is the one that
+ * would be used for the SatelliteManager subscription.
+ *
+ * By watching power state changes, we can detect if the telephony process crashes.
+ *
+ * See b/337258696 for details
+ */
+ private val radioPowerState: StateFlow<Int> =
+ conflatedCallbackFlow {
+ val cb =
+ object : TelephonyCallback(), TelephonyCallback.RadioPowerStateListener {
+ override fun onRadioPowerStateChanged(powerState: Int) {
+ trySend(powerState)
+ }
+ }
+
+ telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), cb)
+
+ awaitClose { telephonyManager.unregisterTelephonyCallback(cb) }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ TelephonyManager.RADIO_POWER_UNAVAILABLE
+ )
+
+ /**
+ * In the event that a telephony phone process has crashed, we expect to see a radio power state
+ * change from ON to something else. This trigger can be used to re-start a flow via
+ * [whenSupported]
+ *
+ * This flow emits [Unit] when started so that newly-started collectors always run, and only
+ * restart when the state goes from ON -> !ON
+ */
+ private val telephonyProcessCrashedEvent: Flow<Unit> =
+ radioPowerState
+ .pairwise()
+ .mapNotNull { (prev: Int, new: Int) ->
+ if (
+ prev == TelephonyManager.RADIO_POWER_ON &&
+ new != TelephonyManager.RADIO_POWER_ON
+ ) {
+ Unit
+ } else {
+ null
+ }
+ }
+ .onStart { emit(Unit) }
+
override val connectionState =
satelliteSupport
.whenSupported(
supported = ::connectionStateFlow,
- orElse = flowOf(SatelliteConnectionState.Off)
+ orElse = flowOf(SatelliteConnectionState.Off),
+ retrySignal = telephonyProcessCrashedEvent,
)
.stateIn(scope, SharingStarted.Eagerly, SatelliteConnectionState.Off)
@@ -232,7 +299,11 @@
override val signalStrength =
satelliteSupport
- .whenSupported(supported = ::signalStrengthFlow, orElse = flowOf(0))
+ .whenSupported(
+ supported = ::signalStrengthFlow,
+ orElse = flowOf(0),
+ retrySignal = telephonyProcessCrashedEvent,
+ )
.stateIn(scope, SharingStarted.Eagerly, 0)
// By using the SupportedSatelliteManager here, we expect registration never to fail
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 332c121..d76fd40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -18,6 +18,7 @@
import android.content.Context
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
@@ -39,6 +40,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
/**
@@ -60,6 +62,7 @@
}
@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
class DeviceBasedSatelliteViewModelImpl
@Inject
constructor(
@@ -124,18 +127,37 @@
shouldActuallyShowIcon,
interactor.connectionState,
) { shouldShow, connectionState ->
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ bool1 = shouldShow
+ str1 = connectionState.name
+ },
+ { "Updating carrier text. shouldActuallyShow=$bool1 connectionState=$str1" }
+ )
if (shouldShow) {
when (connectionState) {
SatelliteConnectionState.On,
SatelliteConnectionState.Connected ->
context.getString(R.string.satellite_connected_carrier_text)
SatelliteConnectionState.Off,
- SatelliteConnectionState.Unknown -> null
+ SatelliteConnectionState.Unknown -> {
+ null
+ }
}
} else {
null
}
}
+ .onEach {
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = it },
+ { "Resulting carrier text = $str1" }
+ )
+ }
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 8b48bd3..2169154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -79,7 +79,8 @@
runnable.run()
return
}
- val fn = "[$label] => AvalancheController.update [${getKey(entry)}]"
+ log { "\n "}
+ val fn = "$label => AvalancheController.update ${getKey(entry)}"
if (entry == null) {
log { "Entry is NULL, stop update." }
return
@@ -88,13 +89,13 @@
debugRunnableLabelMap[runnable] = label
}
if (isShowing(entry)) {
- log { "\n$fn => [update showing]" }
+ log { "\n$fn => update showing" }
runnable.run()
} else if (entry in nextMap) {
- log { "\n$fn => [update next]" }
+ log { "\n$fn => update next" }
nextMap[entry]?.add(runnable)
} else if (headsUpEntryShowing == null) {
- log { "\n$fn => [showNow]" }
+ log { "\n$fn => showNow" }
showNow(entry, arrayListOf(runnable))
} else {
// Clean up invalid state when entry is in list but not map and vice versa
@@ -133,20 +134,22 @@
runnable.run()
return
}
- val fn = "[$label] => AvalancheController.delete " + getKey(entry)
+ log { "\n "}
+ val fn = "$label => AvalancheController.delete " + getKey(entry)
if (entry == null) {
- log { "$fn => cannot remove NULL entry" }
+ log { "$fn => entry NULL, running runnable" }
+ runnable.run()
return
}
if (entry in nextMap) {
- log { "$fn => [remove from next]" }
+ log { "$fn => remove from next" }
if (entry in nextMap) nextMap.remove(entry)
if (entry in nextList) nextList.remove(entry)
} else if (entry in debugDropSet) {
- log { "$fn => [remove from dropset]" }
+ log { "$fn => remove from dropset" }
debugDropSet.remove(entry)
} else if (isShowing(entry)) {
- log { "$fn => [remove showing ${getKey(entry)}]" }
+ log { "$fn => remove showing ${getKey(entry)}" }
previousHunKey = getKey(headsUpEntryShowing)
// Show the next HUN before removing this one, so that we don't tell listeners
// onHeadsUpPinnedModeChanged, which causes
@@ -155,7 +158,7 @@
showNext()
runnable.run()
} else {
- log { "$fn => [removing untracked ${getKey(entry)}]" }
+ log { "$fn => removing untracked ${getKey(entry)}" }
}
logState("after $fn")
}
@@ -239,6 +242,18 @@
return keyList
}
+ fun getWaitingEntry(key: String): HeadsUpEntry? {
+ if (!NotificationThrottleHun.isEnabled) {
+ return null
+ }
+ for (headsUpEntry in nextMap.keys) {
+ if (headsUpEntry.mEntry?.key.equals(key)) {
+ return headsUpEntry
+ }
+ }
+ return null
+ }
+
private fun isShowing(entry: HeadsUpEntry): Boolean {
return headsUpEntryShowing != null && entry.mEntry?.key == headsUpEntryShowing?.mEntry?.key
}
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 a7fe49b..2ee98bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -195,26 +195,29 @@
*/
@Override
public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
- mLogger.logRemoveNotification(key, releaseImmediately);
+ final boolean isWaiting = mAvalancheController.isWaiting(key);
+ mLogger.logRemoveNotification(key, releaseImmediately, isWaiting);
if (mAvalancheController.isWaiting(key)) {
- removeEntry(key);
+ removeEntry(key, "removeNotification (isWaiting)");
return true;
}
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
if (headsUpEntry == null) {
return true;
}
- if (releaseImmediately || canRemoveImmediately(key)) {
- removeEntry(key);
- } else {
- headsUpEntry.removeAsSoonAsPossible();
- return false;
+ if (releaseImmediately) {
+ removeEntry(key, "removeNotification (releaseImmediately)");
+ return true;
}
- return true;
+ if (canRemoveImmediately(key)) {
+ removeEntry(key, "removeNotification (canRemoveImmediately)");
+ return true;
+ }
+ headsUpEntry.removeAsSoonAsPossible();
+ return false;
}
-
/**
* Called when the notification state has been updated.
* @param key the key of the entry that was updated
@@ -245,7 +248,8 @@
if (shouldHeadsUpAgain) {
headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification");
if (headsUpEntry != null) {
- setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
+ setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry),
+ "updateNotificationInternal");
}
}
}
@@ -264,10 +268,10 @@
List<String> waitingKeysToRemove = mAvalancheController.getWaitingKeys();
for (String key : keysToRemove) {
- removeEntry(key);
+ removeEntry(key, "releaseAllImmediately (keysToRemove)");
}
for (String key : waitingKeysToRemove) {
- removeEntry(key);
+ removeEntry(key, "releaseAllImmediately (waitingKeysToRemove)");
}
}
@@ -338,8 +342,9 @@
}
protected void setEntryPinned(
- @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
- mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
+ @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned,
+ String reason) {
+ mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned, reason);
NotificationEntry entry = headsUpEntry.mEntry;
if (!isPinned) {
headsUpEntry.mWasUnpinned = true;
@@ -376,7 +381,7 @@
entry.setHeadsUp(true);
final boolean shouldPin = shouldHeadsUpBecomePinned(entry);
- setEntryPinned(headsUpEntry, shouldPin);
+ setEntryPinned(headsUpEntry, shouldPin, "onEntryAdded");
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, true);
@@ -387,17 +392,24 @@
* Remove a notification from the alerting entries.
* @param key key of notification to remove
*/
- protected final void removeEntry(@NonNull String key) {
+ protected final void removeEntry(@NonNull String key, String reason) {
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
- mLogger.logRemoveEntryRequest(key);
-
+ boolean isWaiting;
+ if (headsUpEntry == null) {
+ headsUpEntry = mAvalancheController.getWaitingEntry(key);
+ isWaiting = true;
+ } else {
+ isWaiting = false;
+ }
+ mLogger.logRemoveEntryRequest(key, reason, isWaiting);
+ HeadsUpEntry finalHeadsUpEntry = headsUpEntry;
Runnable runnable = () -> {
- mLogger.logRemoveEntry(key);
+ mLogger.logRemoveEntry(key, reason, isWaiting);
- if (headsUpEntry == null) {
+ if (finalHeadsUpEntry == null) {
return;
}
- NotificationEntry entry = headsUpEntry.mEntry;
+ NotificationEntry entry = finalHeadsUpEntry.mEntry;
// If the notification is animating, we will remove it at the end of the animation.
if (entry != null && entry.isExpandAnimationRunning()) {
@@ -405,13 +417,13 @@
}
entry.demoteStickyHun();
mHeadsUpEntryMap.remove(key);
- onEntryRemoved(headsUpEntry);
+ onEntryRemoved(finalHeadsUpEntry);
// TODO(b/328390331) move accessibility events to the view layer
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (NotificationsHeadsUpRefactor.isEnabled()) {
- headsUpEntry.cancelAutoRemovalCallbacks("removeEntry");
+ finalHeadsUpEntry.cancelAutoRemovalCallbacks("removeEntry");
} else {
- headsUpEntry.reset();
+ finalHeadsUpEntry.reset();
}
};
mAvalancheController.delete(headsUpEntry, runnable, "removeEntry");
@@ -424,7 +436,7 @@
protected void onEntryRemoved(HeadsUpEntry headsUpEntry) {
NotificationEntry entry = headsUpEntry.mEntry;
entry.setHeadsUp(false);
- setEntryPinned(headsUpEntry, false /* isPinned */);
+ setEntryPinned(headsUpEntry, false /* isPinned */, "onEntryRemoved");
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
mLogger.logNotificationActuallyRemoved(entry);
for (OnHeadsUpChangedListener listener : mListeners) {
@@ -584,7 +596,7 @@
Runnable runnable = () -> {
mLogger.logUnpinEntry(key);
- setEntryPinned(headsUpEntry, false /* isPinned */);
+ setEntryPinned(headsUpEntry, false /* isPinned */, "unpinAll");
// maybe it got un sticky
headsUpEntry.updateEntry(false /* updatePostTime */, "unpinAll");
@@ -985,7 +997,7 @@
/** Creates a runnable to remove this notification from the alerting entries. */
protected Runnable createRemoveRunnable(NotificationEntry entry) {
- return () -> removeEntry(entry.getKey());
+ return () -> removeEntry(entry.getKey(), "createRemoveRunnable");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 11cbc9c..6ffb162 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -121,19 +121,23 @@
})
}
- fun logRemoveEntryRequest(key: String) {
+ fun logRemoveEntryRequest(key: String, reason: String, isWaiting: Boolean) {
buffer.log(TAG, INFO, {
str1 = logKey(key)
+ str2 = reason
+ bool1 = isWaiting
}, {
- "request: remove entry $str1"
+ "request: $str2 => remove entry $str1 isWaiting: $isWaiting"
})
}
- fun logRemoveEntry(key: String) {
+ fun logRemoveEntry(key: String, reason: String, isWaiting: Boolean) {
buffer.log(TAG, INFO, {
str1 = logKey(key)
+ str2 = reason
+ bool1 = isWaiting
}, {
- "remove entry $str1"
+ "$str2 => remove entry $str1 isWaiting: $isWaiting"
})
}
@@ -153,12 +157,13 @@
})
}
- fun logRemoveNotification(key: String, releaseImmediately: Boolean) {
+ fun logRemoveNotification(key: String, releaseImmediately: Boolean, isWaiting: Boolean) {
buffer.log(TAG, INFO, {
str1 = logKey(key)
bool1 = releaseImmediately
+ bool2 = isWaiting
}, {
- "remove notification $str1 releaseImmediately: $bool1"
+ "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2"
})
}
@@ -208,12 +213,13 @@
})
}
- fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean) {
+ fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) {
buffer.log(TAG, VERBOSE, {
str1 = entry.logKey
bool1 = isPinned
+ str2 = reason
}, {
- "set entry pinned $str1 pinned: $bool1"
+ "$str2 => set entry pinned $str1 pinned: $bool1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index f4fc978..1ae5614 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.dagger
+import android.content.ContentResolver
import android.content.Context
import android.media.AudioManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -28,6 +29,7 @@
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.settingslib.volume.shared.AudioManagerEventsReceiverImpl
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -42,21 +44,31 @@
companion object {
@Provides
+ @SysUISingleton
fun provideAudioManagerIntentsReceiver(
@Application context: Context,
@Application coroutineScope: CoroutineScope,
): AudioManagerEventsReceiver = AudioManagerEventsReceiverImpl(context, coroutineScope)
@Provides
+ @SysUISingleton
fun provideAudioRepository(
intentsReceiver: AudioManagerEventsReceiver,
audioManager: AudioManager,
+ contentResolver: ContentResolver,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
): AudioRepository =
- AudioRepositoryImpl(intentsReceiver, audioManager, coroutineContext, coroutineScope)
+ AudioRepositoryImpl(
+ intentsReceiver,
+ audioManager,
+ contentResolver,
+ coroutineContext,
+ coroutineScope,
+ )
@Provides
+ @SysUISingleton
fun provideAudioSharingRepository(
localBluetoothManager: LocalBluetoothManager?,
@Background coroutineContext: CoroutineContext,
@@ -64,10 +76,12 @@
AudioSharingRepositoryImpl(localBluetoothManager, coroutineContext)
@Provides
+ @SysUISingleton
fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor =
AudioModeInteractor(repository)
@Provides
+ @SysUISingleton
fun provideAudioVolumeInteractor(
audioRepository: AudioRepository,
notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
index ea67eea..73f5237 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
@@ -20,6 +20,7 @@
import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
import com.android.settingslib.view.accessibility.data.repository.CaptioningRepositoryImpl
import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -33,6 +34,7 @@
companion object {
@Provides
+ @SysUISingleton
fun provideCaptioningRepository(
captioningManager: CaptioningManager,
@Background coroutineContext: CoroutineContext,
@@ -41,6 +43,7 @@
CaptioningRepositoryImpl(captioningManager, coroutineContext, coroutineScope)
@Provides
+ @SysUISingleton
fun provideCaptioningInteractor(repository: CaptioningRepository): CaptioningInteractor =
CaptioningInteractor(repository)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 3696108..efab199 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -18,7 +18,6 @@
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
@@ -52,12 +51,6 @@
@Provides
@SysUISingleton
- fun provideLocalMediaRepository(
- factory: LocalMediaRepositoryFactory
- ): LocalMediaRepository = factory.create(null)
-
- @Provides
- @SysUISingleton
fun provideMediaDeviceSessionRepository(
intentsReceiver: AudioManagerEventsReceiver,
mediaSessionManager: MediaSessionManager,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
index 4ba7cbb..a11997a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
@@ -21,6 +21,7 @@
import com.android.settingslib.media.data.repository.SpatializerRepository
import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
import dagger.Provides
@@ -33,17 +34,20 @@
companion object {
@Provides
+ @SysUISingleton
fun provideSpatializer(
audioManager: AudioManager,
): Spatializer = audioManager.spatializer
@Provides
+ @SysUISingleton
fun provdieSpatializerRepository(
spatializer: Spatializer,
@Background backgroundContext: CoroutineContext,
): SpatializerRepository = SpatializerRepositoryImpl(spatializer, backgroundContext)
@Provides
+ @SysUISingleton
fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
SpatializerInteractor(repository)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index e052f24..0dc2647 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -18,6 +18,7 @@
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import javax.inject.Inject
@@ -28,6 +29,7 @@
fun create(packageName: String?): LocalMediaRepository
}
+@SysUISingleton
class LocalMediaRepositoryFactoryImpl
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
new file mode 100644
index 0000000..e46ce26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+private const val TAG = "VolumePanelGlobalState"
+
+@SysUISingleton
+class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable {
+
+ private val mutableGlobalState =
+ MutableStateFlow(
+ VolumePanelGlobalState(
+ isVisible = false,
+ )
+ )
+ val globalState: StateFlow<VolumePanelGlobalState> = mutableGlobalState.asStateFlow()
+
+ init {
+ dumpManager.registerNormalDumpable(TAG, this)
+ }
+
+ fun updateVolumePanelState(
+ update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState
+ ) {
+ mutableGlobalState.update(update)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ with(globalState.value) { pw.println("isVisible: $isVisible") }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractor.kt
new file mode 100644
index 0000000..e930aca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractor.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.panel.data.repository.VolumePanelGlobalStateRepository
+import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+@SysUISingleton
+class VolumePanelGlobalStateInteractor
+@Inject
+constructor(
+ private val repository: VolumePanelGlobalStateRepository,
+) {
+
+ val globalState: StateFlow<VolumePanelGlobalState>
+ get() = repository.globalState
+
+ fun setVisible(isVisible: Boolean) {
+ repository.updateVolumePanelState { it.copy(isVisible = isVisible) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/VolumePanelGlobalState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/VolumePanelGlobalState.kt
new file mode 100644
index 0000000..fac49db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/model/VolumePanelGlobalState.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.shared.model
+
+data class VolumePanelGlobalState(val isVisible: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
index f57e293..dc43155 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
@@ -29,7 +29,6 @@
data class VolumePanelState(
@Orientation val orientation: Int,
val isLargeScreen: Boolean,
- val isVisible: Boolean,
) {
init {
require(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index a30de1b..f495a02f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -29,23 +29,22 @@
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.update
// Can't inject a constructor here because VolumePanelComponent provides this view model for its
// components.
@@ -55,6 +54,7 @@
daggerComponentFactory: VolumePanelComponentFactory,
configurationController: ConfigurationController,
broadcastDispatcher: BroadcastDispatcher,
+ private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
) {
private val volumePanelComponent: VolumePanelComponent =
@@ -72,18 +72,12 @@
private val componentsLayoutManager: ComponentsLayoutManager
get() = volumePanelComponent.componentsLayoutManager()
- private val mutablePanelVisibility = MutableStateFlow(true)
-
val volumePanelState: StateFlow<VolumePanelState> =
- combine(
- configurationController.onConfigChanged
- .onStart { emit(resources.configuration) }
- .distinctUntilChanged(),
- mutablePanelVisibility,
- ) { configuration, isVisible ->
+ configurationController.onConfigChanged
+ .onStart { emit(resources.configuration) }
+ .map { configuration ->
VolumePanelState(
orientation = configuration.orientation,
- isVisible = isVisible,
isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
)
}
@@ -92,7 +86,6 @@
SharingStarted.Eagerly,
VolumePanelState(
orientation = resources.configuration.orientation,
- isVisible = mutablePanelVisibility.value,
isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen)
),
)
@@ -126,7 +119,7 @@
}
fun dismissPanel() {
- mutablePanelVisibility.update { false }
+ volumePanelGlobalStateInteractor.setVisible(false)
}
class Factory
@@ -136,6 +129,7 @@
private val daggerComponentFactory: VolumePanelComponentFactory,
private val configurationController: ConfigurationController,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
) {
fun create(coroutineScope: CoroutineScope): VolumePanelViewModel {
@@ -144,7 +138,8 @@
coroutineScope,
daggerComponentFactory,
configurationController,
- broadcastDispatcher
+ broadcastDispatcher,
+ volumePanelGlobalStateInteractor,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 905a749..03c8af90 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -16,26 +16,40 @@
package com.android.systemui.volume.ui.navigation
+import android.app.Dialog
import android.content.Intent
import android.provider.Settings
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import com.android.systemui.statusbar.phone.createBottomSheet
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.volume.VolumePanelFactory
import com.android.systemui.volume.domain.model.VolumePanelRoute
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
class VolumeNavigator
@Inject
constructor(
@@ -46,8 +60,29 @@
private val viewModelFactory: VolumePanelViewModel.Factory,
private val dialogFactory: SystemUIDialogFactory,
private val uiEventLogger: UiEventLogger,
+ private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
) {
+ init {
+ volumePanelGlobalStateInteractor.globalState
+ .map { it.isVisible }
+ .distinctUntilChanged()
+ .flatMapLatest { isVisible ->
+ if (isVisible) {
+ conflatedCallbackFlow<Unit> {
+ val dialog = createNewVolumePanelDialog()
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SHOWN)
+ dialog.show()
+ awaitClose { dialog.dismiss() }
+ }
+ .flowOn(mainContext)
+ } else {
+ emptyFlow()
+ }
+ }
+ .launchIn(applicationScope)
+ }
+
fun openVolumePanel(route: VolumePanelRoute) {
when (route) {
VolumePanelRoute.COMPOSE_VOLUME_PANEL -> showNewVolumePanel()
@@ -62,24 +97,24 @@
}
private fun showNewVolumePanel() {
- applicationScope.launch(mainContext) {
- uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_SHOWN)
- dialogFactory
- .createBottomSheet(
- content = { dialog ->
- LaunchedEffect(dialog) {
- dialog.setOnDismissListener {
- uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_GONE)
- }
- }
+ volumePanelGlobalStateInteractor.setVisible(true)
+ }
- VolumePanelRoot(
- viewModel = viewModelFactory.create(rememberCoroutineScope()),
- onDismiss = { dialog.dismiss() },
- )
- },
+ private fun createNewVolumePanelDialog(): Dialog {
+ return dialogFactory.createBottomSheet(
+ content = { dialog ->
+ LaunchedEffect(dialog) {
+ dialog.setOnDismissListener {
+ uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_GONE)
+ volumePanelGlobalStateInteractor.setVisible(false)
+ }
+ }
+
+ val coroutineScope = rememberCoroutineScope()
+ VolumePanelRoot(
+ remember(coroutineScope) { viewModelFactory.create(coroutineScope) }
)
- .show()
- }
+ },
+ )
}
}
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 472d045..8be1e7a 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
@@ -115,6 +115,7 @@
{ mock(DeviceEntryFingerprintAuthInteractor::class.java) },
{ mock(KeyguardInteractor::class.java) },
{ mock(KeyguardTransitionInteractor::class.java) },
+ { kosmos.sceneInteractor },
testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index fa3fe5c..e02fb29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
@@ -511,7 +512,6 @@
.startedTransition(
to = KeyguardState.LOCKSCREEN,
from = KeyguardState.DOZING,
- ownerName = "FromDozingTransitionInteractor",
animatorAssertion = { it.isNotNull() }
)
@@ -600,7 +600,6 @@
.startedTransition(
to = KeyguardState.PRIMARY_BOUNCER,
from = KeyguardState.DOZING,
- ownerName = "FromDozingTransitionInteractor",
animatorAssertion = { it.isNotNull() }
)
@@ -618,6 +617,7 @@
// WHEN the device wakes up without a keyguard
keyguardRepository.setKeyguardShowing(false)
keyguardRepository.setKeyguardDismissible(true)
+ kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false)
powerInteractor.setAwakeForTest()
advanceTimeBy(60L)
@@ -625,7 +625,6 @@
.startedTransition(
to = KeyguardState.GONE,
from = KeyguardState.DOZING,
- ownerName = "FromDozingTransitionInteractor",
animatorAssertion = { it.isNotNull() }
)
@@ -683,7 +682,6 @@
.startedTransition(
to = KeyguardState.GLANCEABLE_HUB,
from = KeyguardState.DOZING,
- ownerName = FromDozingTransitionInteractor::class.simpleName,
animatorAssertion = { it.isNotNull() }
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 0ec8552..42b81de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -116,6 +116,7 @@
"test_spec:\n" +
" QSTileState(" +
"icon=() -> com.android.systemui.common.shared.model.Icon?, " +
+ "iconRes=null, " +
"label=test_data, " +
"activationState=INACTIVE, " +
"secondaryLabel=null, " +
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
index 4215b8c..e7bde681 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
@@ -109,15 +109,10 @@
activationState: QSTileState.ActivationState,
): QSTileState {
val label = testLabel
+ val iconRes = com.android.internal.R.drawable.stat_sys_managed_profile_status
return QSTileState(
- icon = {
- Icon.Loaded(
- context.getDrawable(
- com.android.internal.R.drawable.stat_sys_managed_profile_status
- )!!,
- null
- )
- },
+ icon = { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes = iconRes,
label = label,
activationState = activationState,
secondaryLabel =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 896c3bf..6f5c56e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -21,41 +21,38 @@
import android.os.Process
import android.os.UserHandle
import android.testing.AndroidTestingRunner
-import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
import org.mockito.kotlin.never
+import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
private val actionExecutor = mock<ActionExecutor>()
- private val accessibilityManager = mock<AccessibilityManager>()
private val uiEventLogger = mock<UiEventLogger>()
+ private val actionsCallback = mock<ScreenshotActionsController.ActionsCallback>()
private val request = ScreenshotData.forTesting()
private val validResult = ScreenshotSavedResult(Uri.EMPTY, Process.myUserHandle(), 0)
- private lateinit var viewModel: ScreenshotViewModel
private lateinit var actionsProvider: ScreenshotActionsProvider
@Before
fun setUp() {
- viewModel = ScreenshotViewModel(accessibilityManager)
request.userHandle = UserHandle.OWNER
}
@@ -63,8 +60,9 @@
fun previewActionAccessed_beforeScreenshotCompleted_doesNothing() {
actionsProvider = createActionsProvider()
- assertNotNull(viewModel.previewAction.value)
- viewModel.previewAction.value!!.invoke()
+ val previewActionCaptor = argumentCaptor<() -> Unit>()
+ verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
+ previewActionCaptor.firstValue.invoke()
verifyNoMoreInteractions(actionExecutor)
}
@@ -72,13 +70,13 @@
fun actionButtonsAccessed_beforeScreenshotCompleted_doesNothing() {
actionsProvider = createActionsProvider()
- assertThat(viewModel.actions.value.size).isEqualTo(2)
- val firstAction = viewModel.actions.value[0]
- assertThat(firstAction.onClicked).isNotNull()
- val secondAction = viewModel.actions.value[1]
- assertThat(secondAction.onClicked).isNotNull()
- firstAction.onClicked!!.invoke()
- secondAction.onClicked!!.invoke()
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
+ verify(actionsCallback, times(2))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+ val firstAction = actionButtonCaptor.firstValue
+ val secondAction = actionButtonCaptor.secondValue
+ firstAction.invoke()
+ secondAction.invoke()
verifyNoMoreInteractions(actionExecutor)
}
@@ -87,29 +85,39 @@
actionsProvider = createActionsProvider()
actionsProvider.setCompletedScreenshot(validResult)
- viewModel.actions.value[0].onClicked!!.invoke()
- verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED), eq(0), eq(""))
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
+ verify(actionsCallback, times(2))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+ actionButtonCaptor.firstValue.invoke()
+
+ verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED), eq(0), eq(""))
val intentCaptor = argumentCaptor<Intent>()
verify(actionExecutor)
- .startSharedTransition(capture(intentCaptor), eq(Process.myUserHandle()), eq(true))
- assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_EDIT)
+ .startSharedTransition(intentCaptor.capture(), eq(Process.myUserHandle()), eq(false))
+ assertThat(intentCaptor.firstValue.action).isEqualTo(Intent.ACTION_CHOOSER)
}
@Test
fun actionAccessed_whilePending_launchesMostRecentAction() = runTest {
actionsProvider = createActionsProvider()
- viewModel.actions.value[0].onClicked!!.invoke()
- viewModel.previewAction.value!!.invoke()
- viewModel.actions.value[1].onClicked!!.invoke()
+ val previewActionCaptor = argumentCaptor<() -> Unit>()
+ verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
+ verify(actionsCallback, times(2))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+
+ actionButtonCaptor.firstValue.invoke()
+ previewActionCaptor.firstValue.invoke()
+ actionButtonCaptor.secondValue.invoke()
actionsProvider.setCompletedScreenshot(validResult)
- verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED), eq(0), eq(""))
+ verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED), eq(0), eq(""))
val intentCaptor = argumentCaptor<Intent>()
verify(actionExecutor)
- .startSharedTransition(capture(intentCaptor), eq(Process.myUserHandle()), eq(false))
- assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CHOOSER)
+ .startSharedTransition(intentCaptor.capture(), eq(Process.myUserHandle()), eq(true))
+ assertThat(intentCaptor.firstValue.action).isEqualTo(Intent.ACTION_EDIT)
}
@Test
@@ -117,9 +125,12 @@
actionsProvider = createActionsProvider()
val onScrollClick = mock<Runnable>()
- val numActions = viewModel.actions.value.size
actionsProvider.onScrollChipReady(onScrollClick)
- viewModel.actions.value[numActions].onClicked!!.invoke()
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
+ // share, edit, scroll
+ verify(actionsCallback, times(3))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+ actionButtonCaptor.thirdValue.invoke()
verify(onScrollClick).run()
}
@@ -129,10 +140,13 @@
actionsProvider = createActionsProvider()
val onScrollClick = mock<Runnable>()
- val numActions = viewModel.actions.value.size
actionsProvider.onScrollChipReady(onScrollClick)
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
actionsProvider.onScrollChipInvalidated()
- viewModel.actions.value[numActions].onClicked!!.invoke()
+ // share, edit, scroll
+ verify(actionsCallback, times(3))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+ actionButtonCaptor.thirdValue.invoke()
verify(onScrollClick, never()).run()
}
@@ -143,11 +157,15 @@
val onScrollClick = mock<Runnable>()
val onScrollClick2 = mock<Runnable>()
- val numActions = viewModel.actions.value.size
+
actionsProvider.onScrollChipReady(onScrollClick)
actionsProvider.onScrollChipInvalidated()
actionsProvider.onScrollChipReady(onScrollClick2)
- viewModel.actions.value[numActions].onClicked!!.invoke()
+ val actionButtonCaptor = argumentCaptor<() -> Unit>()
+ // share, edit, scroll
+ verify(actionsCallback, times(3))
+ .provideActionButton(any(), any(), actionButtonCaptor.capture())
+ actionButtonCaptor.thirdValue.invoke()
verify(onScrollClick2).run()
verify(onScrollClick, never()).run()
@@ -156,11 +174,11 @@
private fun createActionsProvider(): ScreenshotActionsProvider {
return DefaultScreenshotActionsProvider(
context,
- viewModel,
uiEventLogger,
+ UUID.randomUUID(),
request,
- "testid",
- actionExecutor
+ actionExecutor,
+ actionsCallback,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
new file mode 100644
index 0000000..2a3c31a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.screenshot
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
+import java.util.UUID
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScreenshotActionsControllerTest : SysuiTestCase() {
+ private val screenshotData = mock<ScreenshotData>()
+ private val actionExecutor = mock<ActionExecutor>()
+ private val viewModel = mock<ScreenshotViewModel>()
+ private val onClick = mock<() -> Unit>()
+
+ private lateinit var actionsController: ScreenshotActionsController
+ private lateinit var fakeActionsProvider1: FakeActionsProvider
+ private lateinit var fakeActionsProvider2: FakeActionsProvider
+ private val actionsProviderFactory =
+ object : ScreenshotActionsProvider.Factory {
+ var isFirstCall = true
+ override fun create(
+ requestId: UUID,
+ request: ScreenshotData,
+ actionExecutor: ActionExecutor,
+ actionsCallback: ScreenshotActionsController.ActionsCallback
+ ): ScreenshotActionsProvider {
+ return if (isFirstCall) {
+ isFirstCall = false
+ fakeActionsProvider1 = FakeActionsProvider(actionsCallback)
+ fakeActionsProvider1
+ } else {
+ fakeActionsProvider2 = FakeActionsProvider(actionsCallback)
+ fakeActionsProvider2
+ }
+ }
+ }
+
+ @Before
+ fun setUp() {
+ actionsController =
+ ScreenshotActionsController(viewModel, actionsProviderFactory, actionExecutor)
+ }
+
+ @Test
+ fun setPreview_onCurrentScreenshot_updatesViewModel() {
+ actionsController.setCurrentScreenshot(screenshotData)
+ fakeActionsProvider1.callPreview(onClick)
+
+ verify(viewModel).setPreviewAction(onClick)
+ }
+
+ @Test
+ fun setPreview_onNonCurrentScreenshot_doesNotUpdateViewModel() {
+ actionsController.setCurrentScreenshot(screenshotData)
+ actionsController.setCurrentScreenshot(screenshotData)
+ fakeActionsProvider1.callPreview(onClick)
+
+ verify(viewModel, never()).setPreviewAction(any())
+ }
+
+ class FakeActionsProvider(
+ private val actionsCallback: ScreenshotActionsController.ActionsCallback
+ ) : ScreenshotActionsProvider {
+
+ fun callPreview(onClick: () -> Unit) {
+ actionsCallback.providePreviewAction(onClick)
+ }
+
+ override fun onScrollChipReady(onClick: Runnable) {}
+
+ override fun onScrollChipInvalidated() {}
+
+ override fun setCompletedScreenshot(result: ScreenshotSavedResult) {}
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 49a467e..1ef44f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -35,7 +35,7 @@
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
@@ -92,7 +92,7 @@
private lateinit var containerView: View
private lateinit var testableLooper: TestableLooper
- private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var communalRepository: FakeCommunalSceneRepository
private lateinit var underTest: GlanceableHubContainerController
@Before
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 4c2d908..5363c57 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
@@ -46,6 +46,7 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -169,6 +170,7 @@
mActivityStarter,
handler,
TestableLooper.get(this).getLooper(),
+ new ShadeCarrierGroupControllerLogger(FakeLogBuffer.Factory.Companion.create()),
mNetworkController,
mCarrierTextControllerBuilder,
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index fe066ca2..59678a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -20,14 +20,19 @@
import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -65,6 +70,8 @@
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -150,6 +157,10 @@
@Mock
protected BiometricMessageInteractor mBiometricMessageInteractor;
@Mock
+ protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
+ @Mock
+ protected DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
+ @Mock
protected ScreenLifecycle mScreenLifecycle;
@Mock
protected AuthController mAuthController;
@@ -237,6 +248,7 @@
.thenReturn(mock(StateFlow.class));
when(mFaceHelpMessageDeferralFactory.create()).thenReturn(mFaceHelpMessageDeferral);
+ when(mDeviceEntryFingerprintAuthInteractor.isEngaged()).thenReturn(mock(StateFlow.class));
mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor);
@@ -279,7 +291,9 @@
mFlags,
mIndicationHelper,
KeyguardInteractorFactory.create(mFlags).getKeyguardInteractor(),
- mBiometricMessageInteractor
+ mBiometricMessageInteractor,
+ mDeviceEntryFingerprintAuthInteractor,
+ mDeviceEntryFaceAuthInteractor
);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -306,4 +320,22 @@
mExecutor.runAllReady();
reset(mRotateTextViewController);
}
+
+ void verifyNoMessage(int type) {
+ if (type == INDICATION_TYPE_TRANSIENT) {
+ verify(mRotateTextViewController, never()).showTransient(anyString());
+ } else {
+ verify(mRotateTextViewController, never()).updateIndication(eq(type),
+ any(KeyguardIndication.class), anyBoolean());
+ }
+ }
+
+ void verifyIndicationShown(int indicationType, String message) {
+ verify(mRotateTextViewController)
+ .updateIndication(eq(indicationType),
+ mKeyguardIndicationCaptor.capture(),
+ eq(true));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
+ .isEqualTo(message);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index cfe9e2a..80011dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -1514,19 +1514,6 @@
}
@Test
- public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
- createController();
- mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
- "fp not recognized", BiometricSourceType.FINGERPRINT);
- clearInvocations(mRotateTextViewController);
-
- mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
- verifyNoMessage(INDICATION_TYPE_TRUST);
- verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
- }
-
- @Test
public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
createController();
mController.setVisible(true);
@@ -1591,24 +1578,6 @@
verify(mRotateTextViewController).showTransient(eq(message));
}
- private void verifyNoMessage(int type) {
- if (type == INDICATION_TYPE_TRANSIENT) {
- verify(mRotateTextViewController, never()).showTransient(anyString());
- } else {
- verify(mRotateTextViewController, never()).updateIndication(eq(type),
- anyObject(), anyBoolean());
- }
- }
-
- private void verifyIndicationShown(int indicationType, String message) {
- verify(mRotateTextViewController)
- .updateIndication(eq(indicationType),
- mKeyguardIndicationCaptor.capture(),
- eq(true));
- assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
- .isEqualTo(message);
- }
-
private void fingerprintUnlockIsNotPossible() {
setupFingerprintUnlockPossible(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
index 4a14f88..a68ba06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
@@ -21,12 +21,17 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -62,6 +67,33 @@
verify(mIndicationArea, times(3)).visibility = View.VISIBLE
}
+ @Test
+ fun onTrustAgentErrorMessageDelayed_fingerprintEngaged() {
+ createController()
+ mController.setVisible(true)
+
+ // GIVEN fingerprint is engaged
+ whenever(mDeviceEntryFingerprintAuthInteractor.isEngaged).thenReturn(MutableStateFlow(true))
+
+ // WHEN a trust agent error message arrives
+ mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage")
+ mExecutor.runAllReady()
+
+ // THEN no message shows immediately since fingerprint is engaged
+ verifyNoMessage(INDICATION_TYPE_TRUST)
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE)
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP)
+
+ // WHEN fingerprint is no longer engaged
+ whenever(mDeviceEntryFingerprintAuthInteractor.isEngaged)
+ .thenReturn(MutableStateFlow(false))
+ mController.mIsFingerprintEngagedCallback.accept(false)
+ mExecutor.runAllReady()
+
+ // THEN the message will show
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "testMessage")
+ }
+
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
new file mode 100644
index 0000000..25efaf1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.statusbar.chips.screenrecord.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+@SmallTest
+class ScreenRecordChipInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val screenRecordRepo = kosmos.screenRecordRepository
+ private val systemClock = kosmos.fakeSystemClock
+
+ private val underTest = kosmos.screenRecordChipInteractor
+
+ @Test
+ fun chip_doingNothingState_isHidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun chip_startingState_isHidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400)
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun chip_recordingState_isShownWithIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ val icon = (latest as OngoingActivityChipModel.Shown).icon
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+ }
+
+ @Test
+ fun chip_timeResetsOnEachNewRecording() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ systemClock.setElapsedRealtime(1234)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+ systemClock.setElapsedRealtime(5678)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index fa2b343..1260f07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -24,7 +24,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -38,7 +40,7 @@
@Test
fun chip_allHidden_hidden() =
kosmos.testScope.runTest {
- kosmos.screenRecordChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
val latest by collectLastValue(underTest.chip)
@@ -49,28 +51,18 @@
@Test
fun chip_screenRecordShow_restHidden_screenRecordShown() =
kosmos.testScope.runTest {
- val screenRecordChip =
- OngoingActivityChipModel.Shown(
- Icon.Resource(R.drawable.ic_cake, ContentDescription.Loaded("icon")),
- startTimeMs = 500L,
- ) {}
- kosmos.screenRecordChipInteractor.chip.value = screenRecordChip
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
val latest by collectLastValue(underTest.chip)
- assertThat(latest).isEqualTo(screenRecordChip)
+ assertIsScreenRecordChip(latest)
}
@Test
fun chip_screenRecordShowAndCallShow_screenRecordShown() =
kosmos.testScope.runTest {
- val screenRecordChip =
- OngoingActivityChipModel.Shown(
- Icon.Resource(R.drawable.ic_cake, ContentDescription.Loaded("icon")),
- startTimeMs = 500L,
- ) {}
- kosmos.screenRecordChipInteractor.chip.value = screenRecordChip
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
val callChip =
OngoingActivityChipModel.Shown(
@@ -81,13 +73,13 @@
val latest by collectLastValue(underTest.chip)
- assertThat(latest).isEqualTo(screenRecordChip)
+ assertIsScreenRecordChip(latest)
}
@Test
fun chip_screenRecordHideAndCallShown_callShown() =
kosmos.testScope.runTest {
- kosmos.screenRecordChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
val callChip =
OngoingActivityChipModel.Shown(
@@ -111,34 +103,24 @@
startTimeMs = 600L,
) {}
kosmos.callChipInteractor.chip.value = callChip
- kosmos.screenRecordChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
val latest by collectLastValue(underTest.chip)
assertThat(latest).isEqualTo(callChip)
// WHEN the higher priority screen record chip is added
- val screenRecordChip =
- OngoingActivityChipModel.Shown(
- Icon.Resource(R.drawable.ic_cake, ContentDescription.Loaded("icon")),
- startTimeMs = 500L,
- ) {}
- kosmos.screenRecordChipInteractor.chip.value = screenRecordChip
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
// THEN the higher priority screen record chip is used
- assertThat(latest).isEqualTo(screenRecordChip)
+ assertIsScreenRecordChip(latest)
}
@Test
fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
kosmos.testScope.runTest {
// Start with both the higher priority screen record chip and lower priority call chip
- val screenRecordChip =
- OngoingActivityChipModel.Shown(
- Icon.Resource(R.drawable.ic_cake, ContentDescription.Loaded("icon")),
- startTimeMs = 500L,
- ) {}
- kosmos.screenRecordChipInteractor.chip.value = screenRecordChip
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
val callChip =
OngoingActivityChipModel.Shown(
@@ -149,12 +131,18 @@
val latest by collectLastValue(underTest.chip)
- assertThat(latest).isEqualTo(screenRecordChip)
+ assertIsScreenRecordChip(latest)
// WHEN the higher priority screen record is removed
- kosmos.screenRecordChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+ kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
// THEN the lower priority call is used
assertThat(latest).isEqualTo(callChip)
}
+
+ private fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ val icon = (latest as OngoingActivityChipModel.Shown).icon
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 0906d8e..9f752a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -22,7 +22,7 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
-import com.android.systemui.communal.data.repository.communalRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dump.DumpManager
@@ -181,7 +181,7 @@
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Communal)
)
- kosmos.communalRepository.setTransitionState(transitionState)
+ kosmos.communalSceneRepository.setTransitionState(transitionState)
runCurrent()
setDozeAmount(0f)
verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f)
@@ -195,7 +195,7 @@
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Communal)
)
- kosmos.communalRepository.setTransitionState(transitionState)
+ kosmos.communalSceneRepository.setTransitionState(transitionState)
runCurrent()
setDozeAmount(0f)
verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
index 7ca3b1c..6300953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.satellite.data
+import android.telephony.TelephonyManager
import android.telephony.satellite.SatelliteManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -50,11 +51,13 @@
private val demoModeController =
mock<DemoModeController>().apply { whenever(this.isInDemoMode).thenReturn(false) }
private val satelliteManager = mock<SatelliteManager>()
+ private val telephonyManager = mock<TelephonyManager>()
private val systemClock = FakeSystemClock()
private val realImpl =
DeviceBasedSatelliteRepositoryImpl(
Optional.of(satelliteManager),
+ telephonyManager,
testDispatcher,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 6b0ad4b..6651676 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -18,6 +18,8 @@
import android.os.OutcomeReceiver
import android.os.Process
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
import android.telephony.satellite.NtnSignalStrength
import android.telephony.satellite.NtnSignalStrengthCallback
import android.telephony.satellite.SatelliteManager
@@ -36,6 +38,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers
import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME
import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -59,6 +62,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -69,6 +73,7 @@
private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl
@Mock private lateinit var satelliteManager: SatelliteManager
+ @Mock private lateinit var telephonyManager: TelephonyManager
private val systemClock = FakeSystemClock()
private val dispatcher = StandardTestDispatcher()
@@ -86,6 +91,7 @@
underTest =
DeviceBasedSatelliteRepositoryImpl(
Optional.empty(),
+ telephonyManager,
dispatcher,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -362,6 +368,68 @@
verify(satelliteManager).registerForModemStateChanged(any(), any())
}
+ @Test
+ fun telephonyCrash_repoReregistersConnectionStateListener() =
+ testScope.runTest {
+ setupDefaultRepo()
+
+ // GIVEN connection state is requested
+ val connectionState by collectLastValue(underTest.connectionState)
+
+ runCurrent()
+
+ val telephonyCallback =
+ MobileTelephonyHelpers.getTelephonyCallbackForType<
+ TelephonyCallback.RadioPowerStateListener
+ >(
+ telephonyManager
+ )
+
+ // THEN listener is registered once
+ verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
+
+ // WHEN a crash event happens (detected by radio state change)
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+ runCurrent()
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+ runCurrent()
+
+ // THEN listeners are unregistered and re-registered
+ verify(satelliteManager, times(1)).unregisterForModemStateChanged(any())
+ verify(satelliteManager, times(2)).registerForModemStateChanged(any(), any())
+ }
+
+ @Test
+ fun telephonyCrash_repoReregistersSignalStrengthListener() =
+ testScope.runTest {
+ setupDefaultRepo()
+
+ // GIVEN signal strength is requested
+ val signalStrength by collectLastValue(underTest.signalStrength)
+
+ runCurrent()
+
+ val telephonyCallback =
+ MobileTelephonyHelpers.getTelephonyCallbackForType<
+ TelephonyCallback.RadioPowerStateListener
+ >(
+ telephonyManager
+ )
+
+ // THEN listeners are registered the first time
+ verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
+
+ // WHEN a crash event happens (detected by radio state change)
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+ runCurrent()
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+ runCurrent()
+
+ // THEN listeners are unregistered and re-registered
+ verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any())
+ verify(satelliteManager, times(2)).registerForNtnSignalStrengthChanged(any(), any())
+ }
+
private fun setUpRepo(
uptime: Long = MIN_UPTIME,
satMan: SatelliteManager? = satelliteManager,
@@ -380,6 +448,7 @@
underTest =
DeviceBasedSatelliteRepositoryImpl(
if (satMan != null) Optional.of(satMan) else Optional.empty(),
+ telephonyManager,
dispatcher,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 3ded1bb..0f89dcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -152,6 +152,7 @@
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -2300,6 +2301,66 @@
assertThat(bubbleStateListener.mStateChangeCalls).isEqualTo(0);
}
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ public void showBubbleOverflow_hasOverflowContents() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ BubbleStackView stackView = mBubbleController.getStackView();
+ spyOn(stackView);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+
+ verify(stackView).showOverflow(eq(true));
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ public void showBubbleOverflow_isEmpty() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ BubbleStackView stackView = mBubbleController.getStackView();
+ spyOn(stackView);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+ verify(stackView).showOverflow(eq(true));
+
+ // Cancel the bubble so it's removed from the overflow
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+ verify(stackView).showOverflow(eq(false));
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OPTIONAL_BUBBLE_OVERFLOW)
+ @Test
+ public void showBubbleOverflow_ignored() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ BubbleStackView stackView = mBubbleController.getStackView();
+ spyOn(stackView);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+
+ // Cancel the bubble so it's removed from the overflow
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ // Show overflow should never be called if the flag is off
+ verify(stackView, never()).showOverflow(anyBoolean());
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 070a369..f75cdd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -26,15 +26,15 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.statusBarStateController
-import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.util.time.systemClock
-var Kosmos.alternateBouncerInteractor by
+val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
Kosmos.Fixture {
AlternateBouncerInteractor(
statusBarStateController = statusBarStateController,
- keyguardStateController = mock<KeyguardStateControllerImpl>(),
+ keyguardStateController = keyguardStateController,
bouncerRepository = keyguardBouncerRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
biometricSettingsRepository = biometricSettingsRepository,
@@ -44,5 +44,6 @@
keyguardInteractor = { keyguardInteractor },
keyguardTransitionInteractor = { keyguardTransitionInteractor },
scope = testScope.backgroundScope,
+ sceneInteractor = { sceneInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
index 482d60c..c7955c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
@@ -21,7 +21,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
val Kosmos.fakeCommunalRepository by Fixture {
- FakeCommunalRepository(applicationScope = applicationCoroutineScope)
+ FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope)
}
-val Kosmos.communalRepository by Fixture<CommunalRepository> { fakeCommunalRepository }
+val Kosmos.communalSceneRepository by Fixture<CommunalSceneRepository> { fakeCommunalRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
similarity index 88%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
index d958bae..a7bf87d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
@@ -14,14 +14,17 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
-/** Fake implementation of [CommunalRepository]. */
+/** Fake implementation of [CommunalSceneRepository]. */
@OptIn(ExperimentalCoroutinesApi::class)
-class FakeCommunalRepository(
+class FakeCommunalSceneRepository(
applicationScope: CoroutineScope,
override val currentScene: MutableStateFlow<SceneKey> =
MutableStateFlow(CommunalScenes.Default),
-) : CommunalRepository {
- override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
+) : CommunalSceneRepository {
+ override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
+ snapToScene(toScene)
+
+ override fun snapToScene(toScene: SceneKey) {
this.currentScene.value = toScene
this._transitionState.value = flowOf(ObservableTransitionState.Idle(toScene))
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 3fe6973..1583d1c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -20,7 +20,6 @@
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.communalMediaRepository
import com.android.systemui.communal.data.repository.communalPrefsRepository
-import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.flags.Flags
@@ -45,7 +44,7 @@
applicationScope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
broadcastDispatcher = broadcastDispatcher,
- communalRepository = communalRepository,
+ communalSceneInteractor = communalSceneInteractor,
widgetRepository = communalWidgetRepository,
communalPrefsRepository = communalPrefsRepository,
mediaRepository = communalMediaRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
new file mode 100644
index 0000000..ee48c10
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+
+val Kosmos.communalSceneInteractor: CommunalSceneInteractor by
+ Kosmos.Fixture {
+ CommunalSceneInteractor(
+ applicationScope = applicationCoroutineScope,
+ communalSceneRepository = communalSceneRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 93e0b41..d558c96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -40,6 +40,9 @@
private val _isRunning = MutableStateFlow(false)
override val isRunning: Flow<Boolean>
get() = _isRunning
+
+ override val isEngaged: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
fun setIsRunning(value: Boolean) {
_isRunning.value = value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 96a4049..1a45c42 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -125,6 +125,9 @@
private val _isEncryptedOrLockdown = MutableStateFlow(true)
override val isEncryptedOrLockdown: Flow<Boolean> = _isEncryptedOrLockdown
+ private val _isKeyguardEnabled = MutableStateFlow(true)
+ override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
+
override val topClippingBounds = MutableStateFlow<Int?>(null)
override fun setQuickSettingsVisible(isVisible: Boolean) {
@@ -184,6 +187,10 @@
_clockShouldBeCentered.value = shouldBeCentered
}
+ override fun setKeyguardEnabled(enabled: Boolean) {
+ _isKeyguardEnabled.value = enabled
+ }
+
fun dozeTimeTick(millis: Long) {
_dozeTimeTick.value = millis
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index bbe37c1..42af25e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -34,5 +35,6 @@
keyguardInteractor = keyguardInteractor,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ deviceEntryRepository = deviceEntryRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
index 23dcd96..edf77a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -36,5 +37,6 @@
communalInteractor = communalInteractor,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ deviceEntryRepository = deviceEntryRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 604d9e4..4039ee6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
@@ -38,5 +39,7 @@
communalInteractor = communalInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
biometricSettingsRepository = biometricSettingsRepository,
+ keyguardRepository = keyguardRepository,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 7eef704..be8048e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -36,6 +36,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -89,6 +90,7 @@
{ mock(DeviceEntryFingerprintAuthInteractor::class.java) },
{ mock(KeyguardInteractor::class.java) },
{ mock(KeyguardTransitionInteractor::class.java) },
+ { mock(SceneInteractor::class.java) },
testScope.backgroundScope,
)
val powerInteractorWithDeps =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
new file mode 100644
index 0000000..0667a6b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+
+val Kosmos.keyguardEnabledInteractor by
+ Kosmos.Fixture {
+ KeyguardEnabledInteractor(
+ applicationCoroutineScope,
+ keyguardRepository,
+ biometricSettingsRepository,
+ keyguardTransitionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
new file mode 100644
index 0000000..277dbb7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
new file mode 100644
index 0000000..7b9e4a1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.qs.panels.data.repository.iconLabelVisibilityRepository
+
+val Kosmos.iconLabelVisibilityInteractor by
+ Kosmos.Fixture {
+ IconLabelVisibilityInteractor(
+ iconLabelVisibilityRepository,
+ FakeLogBuffer.Factory.create(),
+ applicationCoroutineScope
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
index 34b266a..82cfaf5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
@@ -18,6 +18,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridSizeViewModel
val Kosmos.infiniteGridLayout by
- Kosmos.Fixture { InfiniteGridLayout(iconTilesInteractor, infiniteGridSizeInteractor) }
+ Kosmos.Fixture { InfiniteGridLayout(iconTilesViewModel, infiniteGridSizeViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
index 4febfe91..37c9552 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PartitionedGridLayoutKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
val Kosmos.partitionedGridLayout by
- Kosmos.Fixture { PartitionedGridLayout(iconTilesInteractor, infiniteGridSizeInteractor) }
+ Kosmos.Fixture { PartitionedGridLayout(partitionedGridViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModelKosmos.kt
new file mode 100644
index 0000000..daf6087
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.domain.interactor.iconLabelVisibilityInteractor
+
+val Kosmos.iconLabelVisibilityViewModel by
+ Kosmos.Fixture { IconLabelVisibilityViewModelImpl(iconLabelVisibilityInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModelKosmos.kt
new file mode 100644
index 0000000..89b42a6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModelKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.domain.interactor.iconTilesInteractor
+
+val Kosmos.iconTilesViewModel by Kosmos.Fixture { IconTilesViewModelImpl(iconTilesInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
new file mode 100644
index 0000000..f6dfb8b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.domain.interactor.infiniteGridSizeInteractor
+
+val Kosmos.infiniteGridSizeViewModel by
+ Kosmos.Fixture { InfiniteGridSizeViewModelImpl(infiniteGridSizeInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
new file mode 100644
index 0000000..b07cc7d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.partitionedGridViewModel by
+ Kosmos.Fixture {
+ PartitionedGridViewModel(
+ iconTilesViewModel,
+ infiniteGridSizeViewModel,
+ iconLabelVisibilityViewModel,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
index 4f5c9b4..5b6fd8c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
@@ -45,6 +45,7 @@
other ?: return
}
check("icon").that(actual.icon()).isEqualTo(other.icon())
+ check("iconRes").that(actual.iconRes).isEqualTo(other.iconRes)
check("label").that(actual.label).isEqualTo(other.label)
check("activationState").that(actual.activationState).isEqualTo(other.activationState)
check("secondaryLabel").that(actual.secondaryLabel).isEqualTo(other.secondaryLabel)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt
index cd08274..90d459b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt
@@ -17,15 +17,9 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
-import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import kotlinx.coroutines.flow.MutableStateFlow
-class FakeScreenRecordChipInteractor : ScreenRecordChipInteractor() {
- override val chip: MutableStateFlow<OngoingActivityChipModel> =
- MutableStateFlow(OngoingActivityChipModel.Hidden)
-}
-
class FakeCallChipInteractor : CallChipInteractor() {
override val chip: MutableStateFlow<OngoingActivityChipModel> =
MutableStateFlow(OngoingActivityChipModel.Hidden)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index ffbaa7f..9e02df9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -17,10 +17,20 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
-val Kosmos.screenRecordChipInteractor: FakeScreenRecordChipInteractor by
- Kosmos.Fixture { FakeScreenRecordChipInteractor() }
+val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by
+ Kosmos.Fixture {
+ ScreenRecordChipInteractor(
+ scope = applicationCoroutineScope,
+ screenRecordRepository = screenRecordRepository,
+ systemClock = fakeSystemClock,
+ )
+ }
val Kosmos.callChipInteractor: FakeCallChipInteractor by Kosmos.Fixture { FakeCallChipInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
index 0e909c4..f19ac1e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/KeyguardStateControllerKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import org.mockito.Mockito.mock
-var Kosmos.keyguardStateController by Kosmos.Fixture { mock<KeyguardStateController>() }
+var Kosmos.keyguardStateController: KeyguardStateController by
+ Kosmos.Fixture { mock(KeyguardStateController::class.java) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
new file mode 100644
index 0000000..2ba1211
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumePanelGlobalStateRepository by
+ Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorKosmos.kt
new file mode 100644
index 0000000..6e52364
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/VolumePanelGlobalStateInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
+
+val Kosmos.volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor by
+ Kosmos.Fixture { VolumePanelGlobalStateInteractor(volumePanelGlobalStateRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
index a606588..34a008f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
import com.android.systemui.volume.panel.domain.VolumePanelStartable
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by Kosmos.Fixture { emptySet() }
@@ -36,5 +37,6 @@
volumePanelComponentFactory,
configurationController,
broadcastDispatcher,
+ volumePanelGlobalStateInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorKosmos.kt
index 63b3f23..c3300f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.phone.systemUIDialogFactory
import com.android.systemui.util.mockito.mock
import com.android.systemui.volume.VolumePanelFactory
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
import com.android.systemui.volume.panel.ui.viewmodel.volumePanelViewModelFactory
val Kosmos.volumeNavigator by
@@ -36,5 +37,6 @@
volumePanelViewModelFactory,
systemUIDialogFactory,
uiEventLoggerFake,
+ volumePanelGlobalStateInteractor,
)
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 95cbb6b..48bc803 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -56,11 +56,52 @@
visibility: ["//visibility:public"],
}
+// This and the next module contain the same classes with different implementations.
+// "ravenwood-runtime-common-device" will be statically linked in device side tests.
+// "ravenwood-runtime-common-ravenwood" will only exist in ravenwood-runtime, which will take
+// precedence even if the test jar (accidentally) contains "ravenwood-runtime-common-device".
+// "ravenwood-runtime-common" uses it to detect if the rutime is Ravenwood or not.
+java_library {
+ name: "ravenwood-runtime-common-ravenwood",
+ host_supported: true,
+ sdk_version: "core_current",
+ srcs: [
+ "runtime-common-ravenwood-src/**/*.java",
+ ],
+ visibility: ["//frameworks/base"],
+}
+
+java_library {
+ name: "ravenwood-runtime-common-device",
+ host_supported: true,
+ sdk_version: "core_current",
+ srcs: [
+ "runtime-common-device-src/**/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "ravenwood-runtime-common",
+ host_supported: true,
+ sdk_version: "core_current",
+ srcs: [
+ "runtime-common-src/**/*.java",
+ ],
+ libs: [
+ "ravenwood-runtime-common-ravenwood",
+ ],
+ visibility: ["//visibility:private"],
+}
+
java_library_host {
name: "ravenwood-helper-libcore-runtime.host",
srcs: [
"runtime-helper-src/libcore-fake/**/*.java",
],
+ static_libs: [
+ "ravenwood-runtime-common",
+ ],
visibility: ["//visibility:private"],
}
@@ -77,6 +118,9 @@
srcs: [
"runtime-helper-src/framework/**/*.java",
],
+ static_libs: [
+ "ravenwood-runtime-common",
+ ],
libs: [
"framework-minus-apex.ravenwood",
"ravenwood-junit",
@@ -105,6 +149,7 @@
],
static_libs: [
"androidx.test.monitor-for-device",
+ "ravenwood-runtime-common",
],
libs: [
"android.test.mock",
@@ -145,6 +190,10 @@
"junit-flag-src/**/*.java",
],
sdk_version: "test_current",
+ static_libs: [
+ "ravenwood-runtime-common",
+ "ravenwood-runtime-common-device",
+ ],
libs: [
"junit",
"flag-junit",
@@ -199,7 +248,7 @@
],
srcs: [
- "runtime-helper-src/jni/*.cpp",
+ "runtime-jni/*.cpp",
],
shared_libs: [
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 5506a46..49e793f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -86,10 +86,6 @@
sPendingUncaughtException.compareAndSet(null, throwable);
};
- public static boolean isOnRavenwood() {
- return true;
- }
-
public static void init(RavenwoodRule rule) {
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(false);
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 9d12f85..68b5aeb 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -29,6 +29,8 @@
import android.platform.test.annotations.EnabledOnRavenwood;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -54,7 +56,7 @@
* before a test class is fully initialized.
*/
public class RavenwoodRule implements TestRule {
- static final boolean IS_ON_RAVENWOOD = RavenwoodRuleImpl.isOnRavenwood();
+ static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
/**
* When probing is enabled, all tests will be unconditionally run on Ravenwood to detect
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index c3786ee..5f1b0c2 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -16,6 +16,8 @@
package android.platform.test.ravenwood;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -101,6 +103,8 @@
setValue("ro.soc.model", "Ravenwood");
setValue("ro.debuggable", "1");
+
+ setValue(RAVENWOOD_SYSPROP, "1");
}
/** Copy constructor */
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
index 99ab327..19c1bff 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
@@ -15,9 +15,7 @@
*/
package android.platform.test.ravenwood;
-import java.io.File;
-import java.io.PrintStream;
-import java.util.Arrays;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
/**
* Utilities for writing (bivalent) ravenwood tests.
@@ -26,15 +24,6 @@
private RavenwoodUtils() {
}
- private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
-
- // LibcoreRavenwoodUtils calls it with reflections.
- public static void loadRavenwoodNativeRuntime() {
- if (RavenwoodRule.isOnRavenwood()) {
- RavenwoodUtils.loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
- }
- }
-
/**
* Load a JNI library respecting {@code java.library.path}
* (which reflects {@code LD_LIBRARY_PATH}).
@@ -56,85 +45,6 @@
* it uses {@code JNI_OnLoad()} as the entry point name on both.
*/
public static void loadJniLibrary(String libname) {
- if (RavenwoodRule.isOnRavenwood()) {
- loadLibraryOnRavenwood(libname);
- } else {
- // Just delegate to the loadLibrary().
- System.loadLibrary(libname);
- }
- }
-
- private static void loadLibraryOnRavenwood(String libname) {
- var path = System.getProperty("java.library.path");
- var filename = "lib" + libname + ".so";
-
- System.out.println("Looking for library " + libname + ".so in java.library.path:" + path);
-
- try {
- if (path == null) {
- throw new UnsatisfiedLinkError("Cannot load library " + libname + "."
- + " Property java.library.path not set!");
- }
- for (var dir : path.split(":")) {
- var file = new File(dir + "/" + filename);
- if (file.exists()) {
- System.load(file.getAbsolutePath());
- return;
- }
- }
- throw new UnsatisfiedLinkError("Library " + libname + " not found in "
- + "java.library.path: " + path);
- } catch (Throwable e) {
- dumpFiles(System.out);
- throw e;
- }
- }
-
- private static void dumpFiles(PrintStream out) {
- try {
- var path = System.getProperty("java.library.path");
- out.println("# java.library.path=" + path);
-
- for (var dir : path.split(":")) {
- listFiles(out, new File(dir), "");
-
- var gparent = new File((new File(dir)).getAbsolutePath() + "../../..")
- .getCanonicalFile();
- if (gparent.getName().contains("testcases")) {
- // Special case: if we found this directory, dump its contents too.
- listFiles(out, gparent, "");
- }
- }
-
- var gparent = new File("../..").getCanonicalFile();
- out.println("# ../..=" + gparent);
- listFiles(out, gparent, "");
- } catch (Throwable th) {
- out.println("Error: " + th.toString());
- th.printStackTrace(out);
- }
- }
-
- private static void listFiles(PrintStream out, File dir, String prefix) {
- if (!dir.isDirectory()) {
- out.println(prefix + dir.getAbsolutePath() + " is not a directory!");
- return;
- }
- out.println(prefix + ":" + dir.getAbsolutePath() + "/");
- // First, list the files.
- for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
- out.println(prefix + " " + file.getName() + "" + (file.isDirectory() ? "/" : ""));
- }
-
- // Then recurse.
- if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) {
- // There would be too many files, so don't recurse.
- return;
- }
- for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
- if (file.isDirectory()) {
- listFiles(out, file, prefix + " ");
- }
- }
+ RavenwoodCommonUtils.loadJniLibrary(libname);
}
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 773a89a..483b98a 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -20,10 +20,6 @@
import org.junit.runners.model.Statement;
public class RavenwoodRuleImpl {
- public static boolean isOnRavenwood() {
- return false;
- }
-
public static void init(RavenwoodRule rule) {
// No-op when running on a real device
}
diff --git a/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java b/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
new file mode 100644
index 0000000..1714716
--- /dev/null
+++ b/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwood.common.divergence;
+
+/**
+ * A class that behaves differently on the device side and on Ravenwood, because we have
+ * two build modules with different implementation and we link the different ones at runtime.
+ */
+public final class RavenwoodDivergence {
+ private RavenwoodDivergence() {
+ }
+
+ public static boolean isOnRavenwood() {
+ return false;
+ }
+}
diff --git a/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java b/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
new file mode 100644
index 0000000..59f474a
--- /dev/null
+++ b/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwood.common.divergence;
+
+/**
+ * A class that behaves differently on the device side and on Ravenwood, because we have
+ * two build modules with different implementation and we link the different ones at runtime.
+ */
+public final class RavenwoodDivergence {
+ private RavenwoodDivergence() {
+ }
+
+ public static boolean isOnRavenwood() {
+ return true;
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
new file mode 100644
index 0000000..ee28099
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+/**
+ * Collection of methods to workaround limitation in the hostside JVM.
+ */
+public abstract class JvmWorkaround {
+ JvmWorkaround() {
+ }
+
+ // We only support OpenJDK for now.
+ private static JvmWorkaround sInstance =
+ RavenwoodCommonUtils.isOnRavenwood() ? new OpenJdkWorkaround() : new NullWorkaround();
+
+ public static JvmWorkaround getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Equivalent to Android's FileDescriptor.setInt$().
+ */
+ public abstract void setFdInt(FileDescriptor fd, int fdInt);
+
+
+ /**
+ * Equivalent to Android's FileDescriptor.getInt$().
+ */
+ public abstract int getFdInt(FileDescriptor fd);
+
+ /**
+ * Placeholder implementation for the host side.
+ *
+ * Even on the host side, we don't want to throw just because the class is loaded,
+ * which could cause weird random issues, so we throw from individual methods rather
+ * than from the constructor.
+ */
+ private static class NullWorkaround extends JvmWorkaround {
+ private RuntimeException calledOnHostside() {
+ throw new RuntimeException("This method shouldn't be called on the host side");
+ }
+
+ @Override
+ public void setFdInt(FileDescriptor fd, int fdInt) {
+ throw calledOnHostside();
+ }
+
+ @Override
+ public int getFdInt(FileDescriptor fd) {
+ throw calledOnHostside();
+ }
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
new file mode 100644
index 0000000..9aedaab
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+class OpenJdkWorkaround extends JvmWorkaround {
+ @Override
+ public void setFdInt(FileDescriptor fd, int fdInt) {
+ try {
+ final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+ "getJavaIOFileDescriptorAccess").invoke(null);
+ Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+ "set", FileDescriptor.class, int.class).invoke(obj, fd, fdInt);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+ + " perhaps JRE has changed?", e);
+ }
+ }
+
+ @Override
+ public int getFdInt(FileDescriptor fd) {
+ try {
+ final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+ "getJavaIOFileDescriptorAccess").invoke(null);
+ return (int) Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+ "get", FileDescriptor.class).invoke(obj, fd);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+ + " perhaps JRE has changed?", e);
+ }
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java
new file mode 100644
index 0000000..61d54cb
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java
@@ -0,0 +1,26 @@
+/*
+ * 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.ravenwood.common;
+
+public class RavenwoodBadIntegrityException extends RavenwoodRuntimeException {
+ public RavenwoodBadIntegrityException(String message) {
+ super(message);
+ }
+
+ public RavenwoodBadIntegrityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
new file mode 100644
index 0000000..c8cc8d9
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -0,0 +1,234 @@
+/*
+ * 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.ravenwood.common;
+
+import com.android.ravenwood.common.divergence.RavenwoodDivergence;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+public class RavenwoodCommonUtils {
+ private static final String TAG = "RavenwoodCommonUtils";
+
+ private RavenwoodCommonUtils() {
+ }
+
+ private static final Object sLock = new Object();
+
+ /** Name of `libravenwood_runtime` */
+ private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+
+ /** Directory name of `out/host/linux-x86/testcases/ravenwood-runtime` */
+ private static final String RAVENWOOD_RUNTIME_DIR_NAME = "ravenwood-runtime";
+
+ private static boolean sEnableExtraRuntimeCheck =
+ "1".equals(System.getenv("RAVENWOOD_ENABLE_EXTRA_RUNTIME_CHECK"));
+
+ private static final boolean IS_ON_RAVENWOOD = RavenwoodDivergence.isOnRavenwood();
+
+ private static final String RAVEWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
+
+ public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood";
+
+ // @GuardedBy("sLock")
+ private static boolean sIntegrityChecked = false;
+
+ /**
+ * @return if we're running on Ravenwood.
+ */
+ public static boolean isOnRavenwood() {
+ return IS_ON_RAVENWOOD;
+ }
+
+ /**
+ * Throws if the runtime is not Ravenwood.
+ */
+ public static void ensureOnRavenwood() {
+ if (!isOnRavenwood()) {
+ throw new RavenwoodRuntimeException("This is only supposed to be used on Ravenwood");
+ }
+ }
+
+ /**
+ * @return if the various extra runtime check should be enabled.
+ */
+ public static boolean shouldEnableExtraRuntimeCheck() {
+ return sEnableExtraRuntimeCheck;
+ }
+
+ /**
+ * Load the main runtime JNI library.
+ */
+ public static void loadRavenwoodNativeRuntime() {
+ ensureOnRavenwood();
+ loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
+ }
+
+ /**
+ * Internal implementation of
+ * {@link android.platform.test.ravenwood.RavenwoodUtils#loadJniLibrary(String)}
+ */
+ public static void loadJniLibrary(String libname) {
+ if (RavenwoodCommonUtils.isOnRavenwood()) {
+ loadJniLibraryInternal(libname);
+ } else {
+ System.loadLibrary(libname);
+ }
+ }
+
+ /**
+ * Function equivalent to ART's System.loadLibrary. See RavenwoodUtils for why we need it.
+ */
+ private static void loadJniLibraryInternal(String libname) {
+ var path = System.getProperty("java.library.path");
+ var filename = "lib" + libname + ".so";
+
+ System.out.println("Looking for library " + libname + ".so in java.library.path:" + path);
+
+ try {
+ if (path == null) {
+ throw new UnsatisfiedLinkError("Cannot load library " + libname + "."
+ + " Property java.library.path not set!");
+ }
+ for (var dir : path.split(":")) {
+ var file = new File(dir + "/" + filename);
+ if (file.exists()) {
+ System.load(file.getAbsolutePath());
+ return;
+ }
+ }
+ throw new UnsatisfiedLinkError("Library " + libname + " not found in "
+ + "java.library.path: " + path);
+ } catch (Throwable e) {
+ dumpFiles(System.out);
+ throw e;
+ }
+ }
+
+ private static void dumpFiles(PrintStream out) {
+ try {
+ var path = System.getProperty("java.library.path");
+ out.println("# java.library.path=" + path);
+
+ for (var dir : path.split(":")) {
+ listFiles(out, new File(dir), "");
+
+ var gparent = new File((new File(dir)).getAbsolutePath() + "../../..")
+ .getCanonicalFile();
+ if (gparent.getName().contains("testcases")) {
+ // Special case: if we found this directory, dump its contents too.
+ listFiles(out, gparent, "");
+ }
+ }
+
+ var gparent = new File("../..").getCanonicalFile();
+ out.println("# ../..=" + gparent);
+ listFiles(out, gparent, "");
+ } catch (Throwable th) {
+ out.println("Error: " + th.toString());
+ th.printStackTrace(out);
+ }
+ }
+
+ private static void listFiles(PrintStream out, File dir, String prefix) {
+ if (!dir.isDirectory()) {
+ out.println(prefix + dir.getAbsolutePath() + " is not a directory!");
+ return;
+ }
+ out.println(prefix + ":" + dir.getAbsolutePath() + "/");
+ // First, list the files.
+ for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
+ out.println(prefix + " " + file.getName() + "" + (file.isDirectory() ? "/" : ""));
+ }
+
+ // Then recurse.
+ if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) {
+ // There would be too many files, so don't recurse.
+ return;
+ }
+ for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
+ if (file.isDirectory()) {
+ listFiles(out, file, prefix + " ");
+ }
+ }
+ }
+
+ /**
+ * @return the full directory path that contains the "ravenwood-runtime" files.
+ *
+ * This method throws if called on the device side.
+ */
+ public static String getRavenwoodRuntimePath() {
+ ensureOnRavenwood();
+ return RAVEWOOD_RUNTIME_PATH;
+ }
+
+ private static String getRavenwoodRuntimePathInternal() {
+ if (!isOnRavenwood()) {
+ return null;
+ }
+ var path = System.getProperty("java.library.path");
+
+ System.out.println("Looking for " + RAVENWOOD_RUNTIME_DIR_NAME + " directory"
+ + " in java.library.path:" + path);
+
+ try {
+ if (path == null) {
+ throw new IllegalStateException("java.library.path shouldn't be null");
+ }
+ for (var dir : path.split(":")) {
+
+ // For each path, see if the path contains RAVENWOOD_RUNTIME_DIR_NAME.
+ var d = new File(dir);
+ for (;;) {
+ if (d.getParent() == null) {
+ break; // Root dir, stop.
+ }
+ if (RAVENWOOD_RUNTIME_DIR_NAME.equals(d.getName())) {
+ var ret = d.getAbsolutePath() + "/";
+ System.out.println("Found: " + ret);
+ return ret;
+ }
+ d = d.getParentFile();
+ }
+ }
+ throw new IllegalStateException(RAVENWOOD_RUNTIME_DIR_NAME + " not found");
+ } catch (Throwable e) {
+ dumpFiles(System.out);
+ throw e;
+ }
+ }
+
+ /** Close an {@link AutoCloseable}. */
+ public static void closeQuietly(AutoCloseable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+
+ /** Close a {@link FileDescriptor}. */
+ public static void closeQuietly(FileDescriptor fd) {
+ var is = new FileInputStream(fd);
+ RavenwoodCommonUtils.closeQuietly(is);
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java
new file mode 100644
index 0000000..7b0cebc
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java
@@ -0,0 +1,26 @@
+/*
+ * 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.ravenwood.common;
+
+public class RavenwoodRuntimeException extends RuntimeException {
+ public RavenwoodRuntimeException(String message) {
+ super(message);
+ }
+
+ public RavenwoodRuntimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java
new file mode 100644
index 0000000..6540221
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java
@@ -0,0 +1,71 @@
+/*
+ * 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.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+/**
+ * Class to host all the JNI methods used in ravenwood runtime.
+ */
+public class RavenwoodRuntimeNative {
+ private RavenwoodRuntimeNative() {
+ }
+
+ static {
+ RavenwoodCommonUtils.ensureOnRavenwood();
+ RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
+ }
+
+ public static native void applyFreeFunction(long freeFunction, long nativePtr);
+
+ public static native long nLseek(int fd, long offset, int whence);
+
+ public static native int[] nPipe2(int flags);
+
+ public static native int nDup(int oldfd);
+
+ public static native int nFcntlInt(int fd, int cmd, int arg);
+
+ public static long lseek(FileDescriptor fd, long offset, int whence) {
+ return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
+ }
+
+ public static FileDescriptor[] pipe2(int flags) {
+ var fds = nPipe2(flags);
+ var ret = new FileDescriptor[] {
+ new FileDescriptor(),
+ new FileDescriptor(),
+ };
+ JvmWorkaround.getInstance().setFdInt(ret[0], fds[0]);
+ JvmWorkaround.getInstance().setFdInt(ret[1], fds[1]);
+
+ return ret;
+ }
+
+ public static FileDescriptor dup(FileDescriptor fd) {
+ var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd));
+
+ var retFd = new java.io.FileDescriptor();
+ JvmWorkaround.getInstance().setFdInt(retFd, fdInt);
+ return retFd;
+ }
+
+ public static int fcntlInt(FileDescriptor fd, int cmd, int arg) {
+ var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
+
+ return nFcntlInt(fdInt, cmd, arg);
+ }
+}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
index 2d79914..8fe6853 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
@@ -26,6 +26,7 @@
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
import com.android.internal.annotations.GuardedBy;
+import com.android.ravenwood.common.JvmWorkaround;
import java.io.File;
import java.io.FileDescriptor;
@@ -46,27 +47,11 @@
private static final Map<FileDescriptor, RandomAccessFile> sActive = new HashMap<>();
public static void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
- try {
- final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
- "getJavaIOFileDescriptorAccess").invoke(null);
- Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
- "set", FileDescriptor.class, int.class).invoke(obj, fd, fdInt);
- } catch (ReflectiveOperationException e) {
- throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
- + " perhaps JRE has changed?", e);
- }
+ JvmWorkaround.getInstance().setFdInt(fd, fdInt);
}
public static int native_getFdInt$ravenwood(FileDescriptor fd) {
- try {
- final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
- "getJavaIOFileDescriptorAccess").invoke(null);
- return (int) Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
- "get", FileDescriptor.class).invoke(obj, fd);
- } catch (ReflectiveOperationException e) {
- throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
- + " perhaps JRE has changed?", e);
- }
+ return JvmWorkaround.getInstance().getFdInt(fd);
}
public static FileDescriptor native_open$ravenwood(File file, int pfdMode) throws IOException {
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
index 68bf922..b00cee0 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
@@ -19,6 +19,7 @@
import android.util.Log;
import com.android.internal.ravenwood.RavenwoodEnvironment;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
public class RavenwoodEnvironment_host {
private static final String TAG = RavenwoodEnvironment.TAG;
@@ -39,7 +40,7 @@
if (sInitialized) {
return;
}
- Log.w(TAG, "Initializing Ravenwood environment");
+ Log.i(TAG, "Initializing Ravenwood environment");
// Set the default values.
var sysProps = RavenwoodSystemProperties.DEFAULT_VALUES;
@@ -54,4 +55,4 @@
sInitialized = true;
}
}
-}
+}
\ No newline at end of file
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 69ff262..e198646 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -15,7 +15,7 @@
*/
package com.android.platform.test.ravenwood.runtimehelper;
-import android.platform.test.ravenwood.RavenwoodUtils;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.File;
import java.lang.reflect.Modifier;
@@ -141,7 +141,7 @@
log("Loading " + LIBANDROID_RUNTIME_NAME + " for '" + libanrdoidClasses + "' and '"
+ libhwuiClasses + "'");
- RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
+ RavenwoodCommonUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
}
/**
diff --git a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
deleted file mode 100644
index 8e3a21d..0000000
--- a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <nativehelper/JNIHelp.h>
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-
-typedef void (*FreeFunction)(void*);
-
-static void NativeAllocationRegistry_applyFreeFunction(JNIEnv*,
- jclass,
- jlong freeFunction,
- jlong ptr) {
- void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
- FreeFunction nativeFreeFunction
- = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
- nativeFreeFunction(nativePtr);
-}
-
-static const JNINativeMethod sMethods_NAR[] =
-{
- { "applyFreeFunction", "(JJ)V", (void*)NativeAllocationRegistry_applyFreeFunction },
-};
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
- JNIEnv* env = NULL;
- jint result = -1;
-
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("GetEnv failed!");
- return result;
- }
- ALOG_ASSERT(env, "Could not retrieve the env!");
-
- ALOGI("%s: JNI_OnLoad", __FILE__);
-
- // Initialize the Ravenwood version of NativeAllocationRegistry.
- // We don't use this JNI on the device side, but if we ever have to do, skip this part.
-#ifndef __ANDROID__
- int res = jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry",
- sMethods_NAR, NELEM(sMethods_NAR));
- if (res < 0) {
- return res;
- }
-#endif
-
- return JNI_VERSION_1_4;
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
index 388156a..843455d 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+// [ravenwood] Copied from libcore.
+
package android.system;
import java.io.IOException;
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
new file mode 100644
index 0000000..e031eb2
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.system;
+
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
+
+import java.io.FileDescriptor;
+
+/**
+ * OS class replacement used on Ravenwood. For now, we just implement APIs as we need them...
+ * TODO(b/340887115): Need a better integration with libcore.
+ */
+public final class Os {
+ private Os() {}
+
+ public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
+ return RavenwoodRuntimeNative.lseek(fd, offset, whence);
+ }
+
+
+ public static FileDescriptor[] pipe2(int flags) throws ErrnoException {
+ return RavenwoodRuntimeNative.pipe2(flags);
+ }
+
+ public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
+ return RavenwoodRuntimeNative.dup(fd);
+ }
+
+ public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException {
+ return RavenwoodRuntimeNative.fcntlInt(fd, cmd, arg);
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
new file mode 100644
index 0000000..c56ec8a
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.system;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+/**
+ * Copied from libcore's version, with the local changes:
+ * - All the imports are removed. (they're only used in javadoc)
+ * - All the annotations are removed.
+ * - The initConstants() method is moved to a nested class.
+ *
+ * TODO(b/340887115): Need a better integration with libcore.
+ */
+
+public class OsConstants {
+// @UnsupportedAppUsage
+ private OsConstants() {
+ }
+
+ /**
+ * Returns the index of the element in the {@link StructCapUserData} (cap_user_data)
+ * array that this capability is stored in.
+ *
+ * @param x capability
+ * @return index of the element in the {@link StructCapUserData} array storing this capability
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static int CAP_TO_INDEX(int x) { return x >>> 5; }
+
+ /**
+ * Returns the mask for the given capability. This is relative to the capability's
+ * {@link StructCapUserData} (cap_user_data) element, the index of which can be
+ * retrieved with {@link CAP_TO_INDEX}.
+ *
+ * @param x capability
+ * @return mask for given capability
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static int CAP_TO_MASK(int x) { return 1 << (x & 31); }
+
+ /**
+ * Tests whether the given mode is a block device.
+ */
+ public static boolean S_ISBLK(int mode) { return (mode & S_IFMT) == S_IFBLK; }
+
+ /**
+ * Tests whether the given mode is a character device.
+ */
+ public static boolean S_ISCHR(int mode) { return (mode & S_IFMT) == S_IFCHR; }
+
+ /**
+ * Tests whether the given mode is a directory.
+ */
+ public static boolean S_ISDIR(int mode) { return (mode & S_IFMT) == S_IFDIR; }
+
+ /**
+ * Tests whether the given mode is a FIFO.
+ */
+ public static boolean S_ISFIFO(int mode) { return (mode & S_IFMT) == S_IFIFO; }
+
+ /**
+ * Tests whether the given mode is a regular file.
+ */
+ public static boolean S_ISREG(int mode) { return (mode & S_IFMT) == S_IFREG; }
+
+ /**
+ * Tests whether the given mode is a symbolic link.
+ */
+ public static boolean S_ISLNK(int mode) { return (mode & S_IFMT) == S_IFLNK; }
+
+ /**
+ * Tests whether the given mode is a socket.
+ */
+ public static boolean S_ISSOCK(int mode) { return (mode & S_IFMT) == S_IFSOCK; }
+
+ /**
+ * Extracts the exit status of a child. Only valid if WIFEXITED returns true.
+ */
+ public static int WEXITSTATUS(int status) { return (status & 0xff00) >> 8; }
+
+ /**
+ * Tests whether the child dumped core. Only valid if WIFSIGNALED returns true.
+ */
+ public static boolean WCOREDUMP(int status) { return (status & 0x80) != 0; }
+
+ /**
+ * Returns the signal that caused the child to exit. Only valid if WIFSIGNALED returns true.
+ */
+ public static int WTERMSIG(int status) { return status & 0x7f; }
+
+ /**
+ * Returns the signal that cause the child to stop. Only valid if WIFSTOPPED returns true.
+ */
+ public static int WSTOPSIG(int status) { return WEXITSTATUS(status); }
+
+ /**
+ * Tests whether the child exited normally.
+ */
+ public static boolean WIFEXITED(int status) { return (WTERMSIG(status) == 0); }
+
+ /**
+ * Tests whether the child was stopped (not terminated) by a signal.
+ */
+ public static boolean WIFSTOPPED(int status) { return (WTERMSIG(status) == 0x7f); }
+
+ /**
+ * Tests whether the child was terminated by a signal.
+ */
+ public static boolean WIFSIGNALED(int status) { return (WTERMSIG(status + 1) >= 2); }
+
+ public static final int AF_INET = placeholder();
+ public static final int AF_INET6 = placeholder();
+ public static final int AF_NETLINK = placeholder();
+ public static final int AF_PACKET = placeholder();
+ public static final int AF_UNIX = placeholder();
+
+ /**
+ * The virt-vsock address family, linux specific.
+ * It is used with {@code struct sockaddr_vm} from uapi/linux/vm_sockets.h.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @see VmSocketAddress
+ */
+ public static final int AF_VSOCK = placeholder();
+ public static final int AF_UNSPEC = placeholder();
+ public static final int AI_ADDRCONFIG = placeholder();
+ public static final int AI_ALL = placeholder();
+ public static final int AI_CANONNAME = placeholder();
+ public static final int AI_NUMERICHOST = placeholder();
+ public static final int AI_NUMERICSERV = placeholder();
+ public static final int AI_PASSIVE = placeholder();
+ public static final int AI_V4MAPPED = placeholder();
+ public static final int ARPHRD_ETHER = placeholder();
+
+ /**
+ * The virtio-vsock {@code svmPort} value to bind for any available port.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @see VmSocketAddress
+ */
+ public static final int VMADDR_PORT_ANY = placeholder();
+
+ /**
+ * The virtio-vsock {@code svmCid} value to listens for all CIDs.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @see VmSocketAddress
+ */
+ public static final int VMADDR_CID_ANY = placeholder();
+
+ /**
+ * The virtio-vsock {@code svmCid} value for host communication.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @see VmSocketAddress
+ */
+ public static final int VMADDR_CID_LOCAL = placeholder();
+
+ /**
+ * The virtio-vsock {@code svmCid} value for loopback communication.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @see VmSocketAddress
+ */
+ public static final int VMADDR_CID_HOST = placeholder();
+
+ /**
+ * ARP protocol loopback device identifier.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int ARPHRD_LOOPBACK = placeholder();
+ public static final int CAP_AUDIT_CONTROL = placeholder();
+ public static final int CAP_AUDIT_WRITE = placeholder();
+ public static final int CAP_BLOCK_SUSPEND = placeholder();
+ public static final int CAP_CHOWN = placeholder();
+ public static final int CAP_DAC_OVERRIDE = placeholder();
+ public static final int CAP_DAC_READ_SEARCH = placeholder();
+ public static final int CAP_FOWNER = placeholder();
+ public static final int CAP_FSETID = placeholder();
+ public static final int CAP_IPC_LOCK = placeholder();
+ public static final int CAP_IPC_OWNER = placeholder();
+ public static final int CAP_KILL = placeholder();
+ public static final int CAP_LAST_CAP = placeholder();
+ public static final int CAP_LEASE = placeholder();
+ public static final int CAP_LINUX_IMMUTABLE = placeholder();
+ public static final int CAP_MAC_ADMIN = placeholder();
+ public static final int CAP_MAC_OVERRIDE = placeholder();
+ public static final int CAP_MKNOD = placeholder();
+ public static final int CAP_NET_ADMIN = placeholder();
+ public static final int CAP_NET_BIND_SERVICE = placeholder();
+ public static final int CAP_NET_BROADCAST = placeholder();
+ public static final int CAP_NET_RAW = placeholder();
+ public static final int CAP_SETFCAP = placeholder();
+ public static final int CAP_SETGID = placeholder();
+ public static final int CAP_SETPCAP = placeholder();
+ public static final int CAP_SETUID = placeholder();
+ public static final int CAP_SYS_ADMIN = placeholder();
+ public static final int CAP_SYS_BOOT = placeholder();
+ public static final int CAP_SYS_CHROOT = placeholder();
+ public static final int CAP_SYSLOG = placeholder();
+ public static final int CAP_SYS_MODULE = placeholder();
+ public static final int CAP_SYS_NICE = placeholder();
+ public static final int CAP_SYS_PACCT = placeholder();
+ public static final int CAP_SYS_PTRACE = placeholder();
+ public static final int CAP_SYS_RAWIO = placeholder();
+ public static final int CAP_SYS_RESOURCE = placeholder();
+ public static final int CAP_SYS_TIME = placeholder();
+ public static final int CAP_SYS_TTY_CONFIG = placeholder();
+ public static final int CAP_WAKE_ALARM = placeholder();
+ public static final int E2BIG = placeholder();
+ public static final int EACCES = placeholder();
+ public static final int EADDRINUSE = placeholder();
+ public static final int EADDRNOTAVAIL = placeholder();
+ public static final int EAFNOSUPPORT = placeholder();
+ public static final int EAGAIN = placeholder();
+ public static final int EAI_AGAIN = placeholder();
+ public static final int EAI_BADFLAGS = placeholder();
+ public static final int EAI_FAIL = placeholder();
+ public static final int EAI_FAMILY = placeholder();
+ public static final int EAI_MEMORY = placeholder();
+ public static final int EAI_NODATA = placeholder();
+ public static final int EAI_NONAME = placeholder();
+ public static final int EAI_OVERFLOW = placeholder();
+ public static final int EAI_SERVICE = placeholder();
+ public static final int EAI_SOCKTYPE = placeholder();
+ public static final int EAI_SYSTEM = placeholder();
+ public static final int EALREADY = placeholder();
+ public static final int EBADF = placeholder();
+ public static final int EBADMSG = placeholder();
+ public static final int EBUSY = placeholder();
+ public static final int ECANCELED = placeholder();
+ public static final int ECHILD = placeholder();
+ public static final int ECONNABORTED = placeholder();
+ public static final int ECONNREFUSED = placeholder();
+ public static final int ECONNRESET = placeholder();
+ public static final int EDEADLK = placeholder();
+ public static final int EDESTADDRREQ = placeholder();
+ public static final int EDOM = placeholder();
+ public static final int EDQUOT = placeholder();
+ public static final int EEXIST = placeholder();
+ public static final int EFAULT = placeholder();
+ public static final int EFBIG = placeholder();
+ public static final int EHOSTUNREACH = placeholder();
+ public static final int EIDRM = placeholder();
+ public static final int EILSEQ = placeholder();
+ public static final int EINPROGRESS = placeholder();
+ public static final int EINTR = placeholder();
+ public static final int EINVAL = placeholder();
+ public static final int EIO = placeholder();
+ public static final int EISCONN = placeholder();
+ public static final int EISDIR = placeholder();
+ public static final int ELOOP = placeholder();
+ public static final int EMFILE = placeholder();
+ public static final int EMLINK = placeholder();
+ public static final int EMSGSIZE = placeholder();
+ public static final int EMULTIHOP = placeholder();
+ public static final int ENAMETOOLONG = placeholder();
+ public static final int ENETDOWN = placeholder();
+ public static final int ENETRESET = placeholder();
+ public static final int ENETUNREACH = placeholder();
+ public static final int ENFILE = placeholder();
+ public static final int ENOBUFS = placeholder();
+ public static final int ENODATA = placeholder();
+ public static final int ENODEV = placeholder();
+ public static final int ENOENT = placeholder();
+ public static final int ENOEXEC = placeholder();
+ public static final int ENOLCK = placeholder();
+ public static final int ENOLINK = placeholder();
+ public static final int ENOMEM = placeholder();
+ public static final int ENOMSG = placeholder();
+ public static final int ENONET = placeholder();
+ public static final int ENOPROTOOPT = placeholder();
+ public static final int ENOSPC = placeholder();
+ public static final int ENOSR = placeholder();
+ public static final int ENOSTR = placeholder();
+ public static final int ENOSYS = placeholder();
+ public static final int ENOTCONN = placeholder();
+ public static final int ENOTDIR = placeholder();
+ public static final int ENOTEMPTY = placeholder();
+ public static final int ENOTSOCK = placeholder();
+ public static final int ENOTSUP = placeholder();
+ public static final int ENOTTY = placeholder();
+ public static final int ENXIO = placeholder();
+ public static final int EOPNOTSUPP = placeholder();
+ public static final int EOVERFLOW = placeholder();
+ public static final int EPERM = placeholder();
+ public static final int EPIPE = placeholder();
+ public static final int EPROTO = placeholder();
+ public static final int EPROTONOSUPPORT = placeholder();
+ public static final int EPROTOTYPE = placeholder();
+ public static final int ERANGE = placeholder();
+ public static final int EROFS = placeholder();
+ public static final int ESPIPE = placeholder();
+ public static final int ESRCH = placeholder();
+ public static final int ESTALE = placeholder();
+ public static final int ETH_P_ALL = placeholder();
+ public static final int ETH_P_ARP = placeholder();
+ public static final int ETH_P_IP = placeholder();
+ public static final int ETH_P_IPV6 = placeholder();
+ public static final int ETIME = placeholder();
+ public static final int ETIMEDOUT = placeholder();
+ public static final int ETXTBSY = placeholder();
+ /**
+ * "Too many users" error.
+ * See <a href="https://man7.org/linux/man-pages/man3/errno.3.html">errno(3)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int EUSERS = placeholder();
+ // On Linux, EWOULDBLOCK == EAGAIN. Use EAGAIN instead, to reduce confusion.
+ public static final int EXDEV = placeholder();
+ public static final int EXIT_FAILURE = placeholder();
+ public static final int EXIT_SUCCESS = placeholder();
+ public static final int FD_CLOEXEC = placeholder();
+ public static final int FIONREAD = placeholder();
+ public static final int F_DUPFD = placeholder();
+ public static final int F_DUPFD_CLOEXEC = placeholder();
+ public static final int F_GETFD = placeholder();
+ public static final int F_GETFL = placeholder();
+ public static final int F_GETLK = placeholder();
+ public static final int F_GETLK64 = placeholder();
+ public static final int F_GETOWN = placeholder();
+ public static final int F_OK = placeholder();
+ public static final int F_RDLCK = placeholder();
+ public static final int F_SETFD = placeholder();
+ public static final int F_SETFL = placeholder();
+ public static final int F_SETLK = placeholder();
+ public static final int F_SETLK64 = placeholder();
+ public static final int F_SETLKW = placeholder();
+ public static final int F_SETLKW64 = placeholder();
+ public static final int F_SETOWN = placeholder();
+ public static final int F_UNLCK = placeholder();
+ public static final int F_WRLCK = placeholder();
+ public static final int ICMP_ECHO = placeholder();
+ public static final int ICMP_ECHOREPLY = placeholder();
+ public static final int ICMP6_ECHO_REQUEST = placeholder();
+ public static final int ICMP6_ECHO_REPLY = placeholder();
+ public static final int IFA_F_DADFAILED = placeholder();
+ public static final int IFA_F_DEPRECATED = placeholder();
+ public static final int IFA_F_HOMEADDRESS = placeholder();
+ public static final int IFA_F_MANAGETEMPADDR = placeholder();
+ public static final int IFA_F_NODAD = placeholder();
+ public static final int IFA_F_NOPREFIXROUTE = placeholder();
+ public static final int IFA_F_OPTIMISTIC = placeholder();
+ public static final int IFA_F_PERMANENT = placeholder();
+ public static final int IFA_F_SECONDARY = placeholder();
+ public static final int IFA_F_TEMPORARY = placeholder();
+ public static final int IFA_F_TENTATIVE = placeholder();
+ public static final int IFF_ALLMULTI = placeholder();
+ public static final int IFF_AUTOMEDIA = placeholder();
+ public static final int IFF_BROADCAST = placeholder();
+ public static final int IFF_DEBUG = placeholder();
+ public static final int IFF_DYNAMIC = placeholder();
+ public static final int IFF_LOOPBACK = placeholder();
+ public static final int IFF_MASTER = placeholder();
+ public static final int IFF_MULTICAST = placeholder();
+ public static final int IFF_NOARP = placeholder();
+ public static final int IFF_NOTRAILERS = placeholder();
+ public static final int IFF_POINTOPOINT = placeholder();
+ public static final int IFF_PORTSEL = placeholder();
+ public static final int IFF_PROMISC = placeholder();
+ public static final int IFF_RUNNING = placeholder();
+ public static final int IFF_SLAVE = placeholder();
+ public static final int IFF_UP = placeholder();
+ public static final int IPPROTO_ICMP = placeholder();
+ public static final int IPPROTO_ICMPV6 = placeholder();
+ public static final int IPPROTO_IP = placeholder();
+ public static final int IPPROTO_IPV6 = placeholder();
+ public static final int IPPROTO_RAW = placeholder();
+ public static final int IPPROTO_TCP = placeholder();
+ public static final int IPPROTO_UDP = placeholder();
+
+ /**
+ * Encapsulation Security Payload protocol
+ *
+ * <p>Defined in /uapi/linux/in.h
+ */
+ public static final int IPPROTO_ESP = placeholder();
+
+ public static final int IPV6_CHECKSUM = placeholder();
+ public static final int IPV6_MULTICAST_HOPS = placeholder();
+ public static final int IPV6_MULTICAST_IF = placeholder();
+ public static final int IPV6_MULTICAST_LOOP = placeholder();
+ public static final int IPV6_PKTINFO = placeholder();
+ public static final int IPV6_RECVDSTOPTS = placeholder();
+ public static final int IPV6_RECVHOPLIMIT = placeholder();
+ public static final int IPV6_RECVHOPOPTS = placeholder();
+ public static final int IPV6_RECVPKTINFO = placeholder();
+ public static final int IPV6_RECVRTHDR = placeholder();
+ public static final int IPV6_RECVTCLASS = placeholder();
+ public static final int IPV6_TCLASS = placeholder();
+ public static final int IPV6_UNICAST_HOPS = placeholder();
+ public static final int IPV6_V6ONLY = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int IP_MULTICAST_ALL = placeholder();
+ public static final int IP_MULTICAST_IF = placeholder();
+ public static final int IP_MULTICAST_LOOP = placeholder();
+ public static final int IP_MULTICAST_TTL = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int IP_RECVTOS = placeholder();
+ public static final int IP_TOS = placeholder();
+ public static final int IP_TTL = placeholder();
+ /**
+ * Version constant to be used in {@link StructCapUserHeader} with
+ * {@link Os#capset(StructCapUserHeader, StructCapUserData[])} and
+ * {@link Os#capget(StructCapUserHeader)}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int _LINUX_CAPABILITY_VERSION_3 = placeholder();
+ public static final int MAP_FIXED = placeholder();
+ public static final int MAP_ANONYMOUS = placeholder();
+ /**
+ * Flag argument for {@code mmap(long, long, int, int, FileDescriptor, long)}.
+ *
+ * See <a href="http://man7.org/linux/man-pages/man2/mmap.2.html">mmap(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int MAP_POPULATE = placeholder();
+ public static final int MAP_PRIVATE = placeholder();
+ public static final int MAP_SHARED = placeholder();
+ public static final int MCAST_JOIN_GROUP = placeholder();
+ public static final int MCAST_LEAVE_GROUP = placeholder();
+ public static final int MCAST_JOIN_SOURCE_GROUP = placeholder();
+ public static final int MCAST_LEAVE_SOURCE_GROUP = placeholder();
+ public static final int MCAST_BLOCK_SOURCE = placeholder();
+ public static final int MCAST_UNBLOCK_SOURCE = placeholder();
+ public static final int MCL_CURRENT = placeholder();
+ public static final int MCL_FUTURE = placeholder();
+ public static final int MFD_CLOEXEC = placeholder();
+ public static final int MSG_CTRUNC = placeholder();
+ public static final int MSG_DONTROUTE = placeholder();
+ public static final int MSG_EOR = placeholder();
+ public static final int MSG_OOB = placeholder();
+ public static final int MSG_PEEK = placeholder();
+ public static final int MSG_TRUNC = placeholder();
+ public static final int MSG_WAITALL = placeholder();
+ public static final int MS_ASYNC = placeholder();
+ public static final int MS_INVALIDATE = placeholder();
+ public static final int MS_SYNC = placeholder();
+ public static final int NETLINK_NETFILTER = placeholder();
+ public static final int NETLINK_ROUTE = placeholder();
+ /**
+ * SELinux enforces that only system_server and netd may use this netlink socket type.
+ */
+ public static final int NETLINK_INET_DIAG = placeholder();
+
+ /**
+ * SELinux enforces that only system_server and netd may use this netlink socket type.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/netlink.7.html">netlink(7)</a>
+ */
+ public static final int NETLINK_XFRM = placeholder();
+
+ public static final int NI_DGRAM = placeholder();
+ public static final int NI_NAMEREQD = placeholder();
+ public static final int NI_NOFQDN = placeholder();
+ public static final int NI_NUMERICHOST = placeholder();
+ public static final int NI_NUMERICSERV = placeholder();
+ public static final int O_ACCMODE = placeholder();
+ public static final int O_APPEND = placeholder();
+ public static final int O_CLOEXEC = placeholder();
+ public static final int O_CREAT = placeholder();
+ /**
+ * Flag for {@code Os#open(String, int, int)}.
+ *
+ * When enabled, tries to minimize cache effects of the I/O to and from this
+ * file. In general this will degrade performance, but it is
+ * useful in special situations, such as when applications do
+ * their own caching. File I/O is done directly to/from
+ * user-space buffers. The {@link O_DIRECT} flag on its own makes an
+ * effort to transfer data synchronously, but does not give
+ * the guarantees of the {@link O_SYNC} flag that data and necessary
+ * metadata are transferred. To guarantee synchronous I/O,
+ * {@link O_SYNC} must be used in addition to {@link O_DIRECT}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int O_DIRECT = placeholder();
+ public static final int O_EXCL = placeholder();
+ public static final int O_NOCTTY = placeholder();
+ public static final int O_NOFOLLOW = placeholder();
+ public static final int O_NONBLOCK = placeholder();
+ public static final int O_RDONLY = placeholder();
+ public static final int O_RDWR = placeholder();
+ public static final int O_SYNC = placeholder();
+ public static final int O_DSYNC = placeholder();
+ public static final int O_TRUNC = placeholder();
+ public static final int O_WRONLY = placeholder();
+ public static final int POLLERR = placeholder();
+ public static final int POLLHUP = placeholder();
+ public static final int POLLIN = placeholder();
+ public static final int POLLNVAL = placeholder();
+ public static final int POLLOUT = placeholder();
+ public static final int POLLPRI = placeholder();
+ public static final int POLLRDBAND = placeholder();
+ public static final int POLLRDNORM = placeholder();
+ public static final int POLLWRBAND = placeholder();
+ public static final int POLLWRNORM = placeholder();
+ /**
+ * Reads or changes the ambient capability set of the calling thread.
+ * Has to be used as a first argument for {@link Os#prctl(int, long, long, long, long)}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int PR_CAP_AMBIENT = placeholder();
+ /**
+ * The capability specified in {@code arg3} of {@link Os#prctl(int, long, long, long, long)}
+ * is added to the ambient set. The specified capability must already
+ * be present in both the permitted and the inheritable sets of the process.
+ * Has to be used as a second argument for {@link Os#prctl(int, long, long, long, long)}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int PR_CAP_AMBIENT_RAISE = placeholder();
+ public static final int PR_GET_DUMPABLE = placeholder();
+ public static final int PR_SET_DUMPABLE = placeholder();
+ public static final int PR_SET_NO_NEW_PRIVS = placeholder();
+ public static final int PROT_EXEC = placeholder();
+ public static final int PROT_NONE = placeholder();
+ public static final int PROT_READ = placeholder();
+ public static final int PROT_WRITE = placeholder();
+ public static final int R_OK = placeholder();
+ /**
+ * Specifies a value one greater than the maximum file
+ * descriptor number that can be opened by this process.
+ *
+ * <p>Attempts ({@link Os#open(String, int, int)}, {@link Os#pipe()},
+ * {@link Os#dup(java.io.FileDescriptor)}, etc.) to exceed this
+ * limit yield the error {@link EMFILE}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man3/vlimit.3.html">getrlimit(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int RLIMIT_NOFILE = placeholder();
+ public static final int RT_SCOPE_HOST = placeholder();
+ public static final int RT_SCOPE_LINK = placeholder();
+ public static final int RT_SCOPE_NOWHERE = placeholder();
+ public static final int RT_SCOPE_SITE = placeholder();
+ public static final int RT_SCOPE_UNIVERSE = placeholder();
+ /**
+ * Bitmask for IPv4 addresses add/delete events multicast groups mask.
+ * Used in {@link NetlinkSocketAddress}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man7/netlink.7.html">netlink(7)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int RTMGRP_IPV4_IFADDR = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV4_MROUTE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV4_ROUTE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV4_RULE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV6_IFADDR = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV6_IFINFO = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV6_MROUTE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV6_PREFIX = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_IPV6_ROUTE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_LINK = placeholder();
+ public static final int RTMGRP_NEIGH = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_NOTIFY = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int RTMGRP_TC = placeholder();
+ public static final int SEEK_CUR = placeholder();
+ public static final int SEEK_END = placeholder();
+ public static final int SEEK_SET = placeholder();
+ public static final int SHUT_RD = placeholder();
+ public static final int SHUT_RDWR = placeholder();
+ public static final int SHUT_WR = placeholder();
+ public static final int SIGABRT = placeholder();
+ public static final int SIGALRM = placeholder();
+ public static final int SIGBUS = placeholder();
+ public static final int SIGCHLD = placeholder();
+ public static final int SIGCONT = placeholder();
+ public static final int SIGFPE = placeholder();
+ public static final int SIGHUP = placeholder();
+ public static final int SIGILL = placeholder();
+ public static final int SIGINT = placeholder();
+ public static final int SIGIO = placeholder();
+ public static final int SIGKILL = placeholder();
+ public static final int SIGPIPE = placeholder();
+ public static final int SIGPROF = placeholder();
+ public static final int SIGPWR = placeholder();
+ public static final int SIGQUIT = placeholder();
+ public static final int SIGRTMAX = placeholder();
+ public static final int SIGRTMIN = placeholder();
+ public static final int SIGSEGV = placeholder();
+ public static final int SIGSTKFLT = placeholder();
+ public static final int SIGSTOP = placeholder();
+ public static final int SIGSYS = placeholder();
+ public static final int SIGTERM = placeholder();
+ public static final int SIGTRAP = placeholder();
+ public static final int SIGTSTP = placeholder();
+ public static final int SIGTTIN = placeholder();
+ public static final int SIGTTOU = placeholder();
+ public static final int SIGURG = placeholder();
+ public static final int SIGUSR1 = placeholder();
+ public static final int SIGUSR2 = placeholder();
+ public static final int SIGVTALRM = placeholder();
+ public static final int SIGWINCH = placeholder();
+ public static final int SIGXCPU = placeholder();
+ public static final int SIGXFSZ = placeholder();
+ public static final int SIOCGIFADDR = placeholder();
+ public static final int SIOCGIFBRDADDR = placeholder();
+ public static final int SIOCGIFDSTADDR = placeholder();
+ public static final int SIOCGIFNETMASK = placeholder();
+
+ /**
+ * Set the close-on-exec ({@code FD_CLOEXEC}) flag on the new file
+ * descriptor created by {@link Os#socket(int,int,int)} or
+ * {@link Os#socketpair(int,int,int,java.io.FileDescriptor,java.io.FileDescriptor)}.
+ * See the description of the O_CLOEXEC flag in
+ * <a href="http://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>
+ * for reasons why this may be useful.
+ *
+ * <p>Applications wishing to make use of this flag on older API versions
+ * may use {@link #O_CLOEXEC} instead. On Android, {@code O_CLOEXEC} and
+ * {@code SOCK_CLOEXEC} are the same value.
+ */
+ public static final int SOCK_CLOEXEC = placeholder();
+ public static final int SOCK_DGRAM = placeholder();
+
+ /**
+ * Set the O_NONBLOCK file status flag on the file descriptor
+ * created by {@link Os#socket(int,int,int)} or
+ * {@link Os#socketpair(int,int,int,java.io.FileDescriptor,java.io.FileDescriptor)}.
+ *
+ * <p>Applications wishing to make use of this flag on older API versions
+ * may use {@link #O_NONBLOCK} instead. On Android, {@code O_NONBLOCK}
+ * and {@code SOCK_NONBLOCK} are the same value.
+ */
+ public static final int SOCK_NONBLOCK = placeholder();
+ public static final int SOCK_RAW = placeholder();
+ public static final int SOCK_SEQPACKET = placeholder();
+ public static final int SOCK_STREAM = placeholder();
+ public static final int SOL_SOCKET = placeholder();
+ public static final int SOL_UDP = placeholder();
+ public static final int SOL_PACKET = placeholder();
+ public static final int SO_BINDTODEVICE = placeholder();
+ public static final int SO_BROADCAST = placeholder();
+ public static final int SO_DEBUG = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int SO_DOMAIN = placeholder();
+ public static final int SO_DONTROUTE = placeholder();
+ public static final int SO_ERROR = placeholder();
+ public static final int SO_KEEPALIVE = placeholder();
+ public static final int SO_LINGER = placeholder();
+ public static final int SO_OOBINLINE = placeholder();
+ public static final int SO_PASSCRED = placeholder();
+ public static final int SO_PEERCRED = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int SO_PROTOCOL = placeholder();
+ public static final int SO_RCVBUF = placeholder();
+ public static final int SO_RCVLOWAT = placeholder();
+ public static final int SO_RCVTIMEO = placeholder();
+ public static final int SO_REUSEADDR = placeholder();
+ public static final int SO_SNDBUF = placeholder();
+ public static final int SO_SNDLOWAT = placeholder();
+ public static final int SO_SNDTIMEO = placeholder();
+ public static final int SO_TYPE = placeholder();
+ public static final int PACKET_IGNORE_OUTGOING = placeholder();
+ /**
+ * Bitmask for flags argument of
+ * {@link splice(java.io.FileDescriptor, Int64Ref , FileDescriptor, Int64Ref, long, int)}.
+ *
+ * Attempt to move pages instead of copying. This is only a
+ * hint to the kernel: pages may still be copied if the
+ * kernel cannot move the pages from the pipe, or if the pipe
+ * buffers don't refer to full pages.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int SPLICE_F_MOVE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int SPLICE_F_NONBLOCK = placeholder();
+ /**
+ * Bitmask for flags argument of
+ * {@link splice(java.io.FileDescriptor, Int64Ref, FileDescriptor, Int64Ref, long, int)}.
+ *
+ * <p>Indicates that more data will be coming in a subsequent splice. This is
+ * a helpful hint when the {@code fdOut} refers to a socket.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int SPLICE_F_MORE = placeholder();
+ public static final int STDERR_FILENO = placeholder();
+ public static final int STDIN_FILENO = placeholder();
+ public static final int STDOUT_FILENO = placeholder();
+ public static final int ST_MANDLOCK = placeholder();
+ public static final int ST_NOATIME = placeholder();
+ public static final int ST_NODEV = placeholder();
+ public static final int ST_NODIRATIME = placeholder();
+ public static final int ST_NOEXEC = placeholder();
+ public static final int ST_NOSUID = placeholder();
+ public static final int ST_RDONLY = placeholder();
+ public static final int ST_RELATIME = placeholder();
+ public static final int ST_SYNCHRONOUS = placeholder();
+ public static final int S_IFBLK = placeholder();
+ public static final int S_IFCHR = placeholder();
+ public static final int S_IFDIR = placeholder();
+ public static final int S_IFIFO = placeholder();
+ public static final int S_IFLNK = placeholder();
+ public static final int S_IFMT = placeholder();
+ public static final int S_IFREG = placeholder();
+ public static final int S_IFSOCK = placeholder();
+ public static final int S_IRGRP = placeholder();
+ public static final int S_IROTH = placeholder();
+ public static final int S_IRUSR = placeholder();
+ public static final int S_IRWXG = placeholder();
+ public static final int S_IRWXO = placeholder();
+ public static final int S_IRWXU = placeholder();
+ public static final int S_ISGID = placeholder();
+ public static final int S_ISUID = placeholder();
+ public static final int S_ISVTX = placeholder();
+ public static final int S_IWGRP = placeholder();
+ public static final int S_IWOTH = placeholder();
+ public static final int S_IWUSR = placeholder();
+ public static final int S_IXGRP = placeholder();
+ public static final int S_IXOTH = placeholder();
+ public static final int S_IXUSR = placeholder();
+ public static final int TCP_NODELAY = placeholder();
+ public static final int TCP_USER_TIMEOUT = placeholder();
+ public static final int UDP_GRO = placeholder();
+ public static final int UDP_SEGMENT = placeholder();
+ /**
+ * Get the number of bytes in the output buffer.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/ioctl.2.html">ioctl(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int TIOCOUTQ = placeholder();
+ /**
+ * Sockopt option to encapsulate ESP packets in UDP.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int UDP_ENCAP = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int UDP_ENCAP_ESPINUDP_NON_IKE = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int UDP_ENCAP_ESPINUDP = placeholder();
+ /** @hide */
+// @UnsupportedAppUsage
+ public static final int UNIX_PATH_MAX = placeholder();
+ public static final int WCONTINUED = placeholder();
+ public static final int WEXITED = placeholder();
+ public static final int WNOHANG = placeholder();
+ public static final int WNOWAIT = placeholder();
+ public static final int WSTOPPED = placeholder();
+ public static final int WUNTRACED = placeholder();
+ public static final int W_OK = placeholder();
+ /**
+ * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+ *
+ * <p>Performs a pure create, which fails if the named attribute exists already.
+ *
+ * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int XATTR_CREATE = placeholder();
+ /**
+ * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+ *
+ * <p>Perform a pure replace operation, which fails if the named attribute
+ * does not already exist.
+ *
+ * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+ *
+ * @hide
+ */
+// @UnsupportedAppUsage
+// @SystemApi(client = MODULE_LIBRARIES)
+ public static final int XATTR_REPLACE = placeholder();
+ public static final int X_OK = placeholder();
+ public static final int _SC_2_CHAR_TERM = placeholder();
+ public static final int _SC_2_C_BIND = placeholder();
+ public static final int _SC_2_C_DEV = placeholder();
+ public static final int _SC_2_C_VERSION = placeholder();
+ public static final int _SC_2_FORT_DEV = placeholder();
+ public static final int _SC_2_FORT_RUN = placeholder();
+ public static final int _SC_2_LOCALEDEF = placeholder();
+ public static final int _SC_2_SW_DEV = placeholder();
+ public static final int _SC_2_UPE = placeholder();
+ public static final int _SC_2_VERSION = placeholder();
+ public static final int _SC_AIO_LISTIO_MAX = placeholder();
+ public static final int _SC_AIO_MAX = placeholder();
+ public static final int _SC_AIO_PRIO_DELTA_MAX = placeholder();
+ public static final int _SC_ARG_MAX = placeholder();
+ public static final int _SC_ASYNCHRONOUS_IO = placeholder();
+ public static final int _SC_ATEXIT_MAX = placeholder();
+ public static final int _SC_AVPHYS_PAGES = placeholder();
+ public static final int _SC_BC_BASE_MAX = placeholder();
+ public static final int _SC_BC_DIM_MAX = placeholder();
+ public static final int _SC_BC_SCALE_MAX = placeholder();
+ public static final int _SC_BC_STRING_MAX = placeholder();
+ public static final int _SC_CHILD_MAX = placeholder();
+ public static final int _SC_CLK_TCK = placeholder();
+ public static final int _SC_COLL_WEIGHTS_MAX = placeholder();
+ public static final int _SC_DELAYTIMER_MAX = placeholder();
+ public static final int _SC_EXPR_NEST_MAX = placeholder();
+ public static final int _SC_FSYNC = placeholder();
+ public static final int _SC_GETGR_R_SIZE_MAX = placeholder();
+ public static final int _SC_GETPW_R_SIZE_MAX = placeholder();
+ public static final int _SC_IOV_MAX = placeholder();
+ public static final int _SC_JOB_CONTROL = placeholder();
+ public static final int _SC_LINE_MAX = placeholder();
+ public static final int _SC_LOGIN_NAME_MAX = placeholder();
+ public static final int _SC_MAPPED_FILES = placeholder();
+ public static final int _SC_MEMLOCK = placeholder();
+ public static final int _SC_MEMLOCK_RANGE = placeholder();
+ public static final int _SC_MEMORY_PROTECTION = placeholder();
+ public static final int _SC_MESSAGE_PASSING = placeholder();
+ public static final int _SC_MQ_OPEN_MAX = placeholder();
+ public static final int _SC_MQ_PRIO_MAX = placeholder();
+ public static final int _SC_NGROUPS_MAX = placeholder();
+ public static final int _SC_NPROCESSORS_CONF = placeholder();
+ public static final int _SC_NPROCESSORS_ONLN = placeholder();
+ public static final int _SC_OPEN_MAX = placeholder();
+ public static final int _SC_PAGESIZE = placeholder();
+ public static final int _SC_PAGE_SIZE = placeholder();
+ public static final int _SC_PASS_MAX = placeholder();
+ public static final int _SC_PHYS_PAGES = placeholder();
+ public static final int _SC_PRIORITIZED_IO = placeholder();
+ public static final int _SC_PRIORITY_SCHEDULING = placeholder();
+ public static final int _SC_REALTIME_SIGNALS = placeholder();
+ public static final int _SC_RE_DUP_MAX = placeholder();
+ public static final int _SC_RTSIG_MAX = placeholder();
+ public static final int _SC_SAVED_IDS = placeholder();
+ public static final int _SC_SEMAPHORES = placeholder();
+ public static final int _SC_SEM_NSEMS_MAX = placeholder();
+ public static final int _SC_SEM_VALUE_MAX = placeholder();
+ public static final int _SC_SHARED_MEMORY_OBJECTS = placeholder();
+ public static final int _SC_SIGQUEUE_MAX = placeholder();
+ public static final int _SC_STREAM_MAX = placeholder();
+ public static final int _SC_SYNCHRONIZED_IO = placeholder();
+ public static final int _SC_THREADS = placeholder();
+ public static final int _SC_THREAD_ATTR_STACKADDR = placeholder();
+ public static final int _SC_THREAD_ATTR_STACKSIZE = placeholder();
+ public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS = placeholder();
+ public static final int _SC_THREAD_KEYS_MAX = placeholder();
+ public static final int _SC_THREAD_PRIORITY_SCHEDULING = placeholder();
+ public static final int _SC_THREAD_PRIO_INHERIT = placeholder();
+ public static final int _SC_THREAD_PRIO_PROTECT = placeholder();
+ public static final int _SC_THREAD_SAFE_FUNCTIONS = placeholder();
+ public static final int _SC_THREAD_STACK_MIN = placeholder();
+ public static final int _SC_THREAD_THREADS_MAX = placeholder();
+ public static final int _SC_TIMERS = placeholder();
+ public static final int _SC_TIMER_MAX = placeholder();
+ public static final int _SC_TTY_NAME_MAX = placeholder();
+ public static final int _SC_TZNAME_MAX = placeholder();
+ public static final int _SC_VERSION = placeholder();
+ public static final int _SC_XBS5_ILP32_OFF32 = placeholder();
+ public static final int _SC_XBS5_ILP32_OFFBIG = placeholder();
+ public static final int _SC_XBS5_LP64_OFF64 = placeholder();
+ public static final int _SC_XBS5_LPBIG_OFFBIG = placeholder();
+ public static final int _SC_XOPEN_CRYPT = placeholder();
+ public static final int _SC_XOPEN_ENH_I18N = placeholder();
+ public static final int _SC_XOPEN_LEGACY = placeholder();
+ public static final int _SC_XOPEN_REALTIME = placeholder();
+ public static final int _SC_XOPEN_REALTIME_THREADS = placeholder();
+ public static final int _SC_XOPEN_SHM = placeholder();
+ public static final int _SC_XOPEN_UNIX = placeholder();
+ public static final int _SC_XOPEN_VERSION = placeholder();
+ public static final int _SC_XOPEN_XCU_VERSION = placeholder();
+
+ /**
+ * Returns the string name of a getaddrinfo(3) error value.
+ * For example, "EAI_AGAIN".
+ */
+ public static String gaiName(int error) {
+ if (error == EAI_AGAIN) {
+ return "EAI_AGAIN";
+ }
+ if (error == EAI_BADFLAGS) {
+ return "EAI_BADFLAGS";
+ }
+ if (error == EAI_FAIL) {
+ return "EAI_FAIL";
+ }
+ if (error == EAI_FAMILY) {
+ return "EAI_FAMILY";
+ }
+ if (error == EAI_MEMORY) {
+ return "EAI_MEMORY";
+ }
+ if (error == EAI_NODATA) {
+ return "EAI_NODATA";
+ }
+ if (error == EAI_NONAME) {
+ return "EAI_NONAME";
+ }
+ if (error == EAI_OVERFLOW) {
+ return "EAI_OVERFLOW";
+ }
+ if (error == EAI_SERVICE) {
+ return "EAI_SERVICE";
+ }
+ if (error == EAI_SOCKTYPE) {
+ return "EAI_SOCKTYPE";
+ }
+ if (error == EAI_SYSTEM) {
+ return "EAI_SYSTEM";
+ }
+ return null;
+ }
+
+ /**
+ * Returns the string name of an errno value.
+ * For example, "EACCES". See {@link Os#strerror} for human-readable errno descriptions.
+ */
+ public static String errnoName(int errno) {
+ if (errno == E2BIG) {
+ return "E2BIG";
+ }
+ if (errno == EACCES) {
+ return "EACCES";
+ }
+ if (errno == EADDRINUSE) {
+ return "EADDRINUSE";
+ }
+ if (errno == EADDRNOTAVAIL) {
+ return "EADDRNOTAVAIL";
+ }
+ if (errno == EAFNOSUPPORT) {
+ return "EAFNOSUPPORT";
+ }
+ if (errno == EAGAIN) {
+ return "EAGAIN";
+ }
+ if (errno == EALREADY) {
+ return "EALREADY";
+ }
+ if (errno == EBADF) {
+ return "EBADF";
+ }
+ if (errno == EBADMSG) {
+ return "EBADMSG";
+ }
+ if (errno == EBUSY) {
+ return "EBUSY";
+ }
+ if (errno == ECANCELED) {
+ return "ECANCELED";
+ }
+ if (errno == ECHILD) {
+ return "ECHILD";
+ }
+ if (errno == ECONNABORTED) {
+ return "ECONNABORTED";
+ }
+ if (errno == ECONNREFUSED) {
+ return "ECONNREFUSED";
+ }
+ if (errno == ECONNRESET) {
+ return "ECONNRESET";
+ }
+ if (errno == EDEADLK) {
+ return "EDEADLK";
+ }
+ if (errno == EDESTADDRREQ) {
+ return "EDESTADDRREQ";
+ }
+ if (errno == EDOM) {
+ return "EDOM";
+ }
+ if (errno == EDQUOT) {
+ return "EDQUOT";
+ }
+ if (errno == EEXIST) {
+ return "EEXIST";
+ }
+ if (errno == EFAULT) {
+ return "EFAULT";
+ }
+ if (errno == EFBIG) {
+ return "EFBIG";
+ }
+ if (errno == EHOSTUNREACH) {
+ return "EHOSTUNREACH";
+ }
+ if (errno == EIDRM) {
+ return "EIDRM";
+ }
+ if (errno == EILSEQ) {
+ return "EILSEQ";
+ }
+ if (errno == EINPROGRESS) {
+ return "EINPROGRESS";
+ }
+ if (errno == EINTR) {
+ return "EINTR";
+ }
+ if (errno == EINVAL) {
+ return "EINVAL";
+ }
+ if (errno == EIO) {
+ return "EIO";
+ }
+ if (errno == EISCONN) {
+ return "EISCONN";
+ }
+ if (errno == EISDIR) {
+ return "EISDIR";
+ }
+ if (errno == ELOOP) {
+ return "ELOOP";
+ }
+ if (errno == EMFILE) {
+ return "EMFILE";
+ }
+ if (errno == EMLINK) {
+ return "EMLINK";
+ }
+ if (errno == EMSGSIZE) {
+ return "EMSGSIZE";
+ }
+ if (errno == EMULTIHOP) {
+ return "EMULTIHOP";
+ }
+ if (errno == ENAMETOOLONG) {
+ return "ENAMETOOLONG";
+ }
+ if (errno == ENETDOWN) {
+ return "ENETDOWN";
+ }
+ if (errno == ENETRESET) {
+ return "ENETRESET";
+ }
+ if (errno == ENETUNREACH) {
+ return "ENETUNREACH";
+ }
+ if (errno == ENFILE) {
+ return "ENFILE";
+ }
+ if (errno == ENOBUFS) {
+ return "ENOBUFS";
+ }
+ if (errno == ENODATA) {
+ return "ENODATA";
+ }
+ if (errno == ENODEV) {
+ return "ENODEV";
+ }
+ if (errno == ENOENT) {
+ return "ENOENT";
+ }
+ if (errno == ENOEXEC) {
+ return "ENOEXEC";
+ }
+ if (errno == ENOLCK) {
+ return "ENOLCK";
+ }
+ if (errno == ENOLINK) {
+ return "ENOLINK";
+ }
+ if (errno == ENOMEM) {
+ return "ENOMEM";
+ }
+ if (errno == ENOMSG) {
+ return "ENOMSG";
+ }
+ if (errno == ENONET) {
+ return "ENONET";
+ }
+ if (errno == ENOPROTOOPT) {
+ return "ENOPROTOOPT";
+ }
+ if (errno == ENOSPC) {
+ return "ENOSPC";
+ }
+ if (errno == ENOSR) {
+ return "ENOSR";
+ }
+ if (errno == ENOSTR) {
+ return "ENOSTR";
+ }
+ if (errno == ENOSYS) {
+ return "ENOSYS";
+ }
+ if (errno == ENOTCONN) {
+ return "ENOTCONN";
+ }
+ if (errno == ENOTDIR) {
+ return "ENOTDIR";
+ }
+ if (errno == ENOTEMPTY) {
+ return "ENOTEMPTY";
+ }
+ if (errno == ENOTSOCK) {
+ return "ENOTSOCK";
+ }
+ if (errno == ENOTSUP) {
+ return "ENOTSUP";
+ }
+ if (errno == ENOTTY) {
+ return "ENOTTY";
+ }
+ if (errno == ENXIO) {
+ return "ENXIO";
+ }
+ if (errno == EOPNOTSUPP) {
+ return "EOPNOTSUPP";
+ }
+ if (errno == EOVERFLOW) {
+ return "EOVERFLOW";
+ }
+ if (errno == EPERM) {
+ return "EPERM";
+ }
+ if (errno == EPIPE) {
+ return "EPIPE";
+ }
+ if (errno == EPROTO) {
+ return "EPROTO";
+ }
+ if (errno == EPROTONOSUPPORT) {
+ return "EPROTONOSUPPORT";
+ }
+ if (errno == EPROTOTYPE) {
+ return "EPROTOTYPE";
+ }
+ if (errno == ERANGE) {
+ return "ERANGE";
+ }
+ if (errno == EROFS) {
+ return "EROFS";
+ }
+ if (errno == ESPIPE) {
+ return "ESPIPE";
+ }
+ if (errno == ESRCH) {
+ return "ESRCH";
+ }
+ if (errno == ESTALE) {
+ return "ESTALE";
+ }
+ if (errno == ETIME) {
+ return "ETIME";
+ }
+ if (errno == ETIMEDOUT) {
+ return "ETIMEDOUT";
+ }
+ if (errno == ETXTBSY) {
+ return "ETXTBSY";
+ }
+ if (errno == EXDEV) {
+ return "EXDEV";
+ }
+ return null;
+ }
+
+ // [ravenwood-change] Moved to a nested class.
+ // @UnsupportedAppUsage
+ static class Native {
+ private static native void initConstants();
+ }
+
+ // A hack to avoid these constants being inlined by javac...
+// @UnsupportedAppUsage
+ private static int placeholder() { return 0; }
+ // ...because we want to initialize them at runtime.
+ static {
+ // [ravenwood-change] Load the JNI lib.
+ RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
+ Native.initConstants();
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
deleted file mode 100644
index 839b62a..0000000
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.ravenwood;
-
-public class LibcoreRavenwoodUtils {
- private LibcoreRavenwoodUtils() {
- }
-
- public static void loadRavenwoodNativeRuntime() {
- // TODO Stop using reflections.
- // We need to call RavenwoodUtils.loadRavenwoodNativeRuntime(), but due to the build
- // structure complexity, we can't refer to to this method directly from here,
- // so let's use reflections for now...
- try {
- final var clazz = Class.forName("android.platform.test.ravenwood.RavenwoodUtils");
- final var method = clazz.getMethod("loadRavenwoodNativeRuntime");
- method.invoke(null);
- } catch (Throwable th) {
- throw new IllegalStateException("Failed to load Ravenwood native runtime", th);
- }
- }
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
index 93861e8..14b5a4f 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -15,7 +15,7 @@
*/
package libcore.util;
-import libcore.ravenwood.LibcoreRavenwoodUtils;
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
@@ -27,11 +27,6 @@
* (Should ART switch to java.lang.ref.Cleaner?)
*/
public class NativeAllocationRegistry {
- static {
- // Initialize the JNI method.
- LibcoreRavenwoodUtils.loadRavenwoodNativeRuntime();
- }
-
private final long mFreeFunction;
private static final Cleaner sCleaner = Cleaner.create();
@@ -71,7 +66,7 @@
}
final Runnable releaser = () -> {
- applyFreeFunction(mFreeFunction, nativePtr);
+ RavenwoodRuntimeNative.applyFreeFunction(mFreeFunction, nativePtr);
};
sCleaner.register(referent, releaser);
@@ -79,10 +74,4 @@
Reference.reachabilityFence(referent);
return releaser;
}
-
- /**
- * Calls {@code freeFunction}({@code nativePtr}).
- */
- public static native void applyFreeFunction(long freeFunction, long nativePtr);
}
-
diff --git a/ravenwood/runtime-jni/ravenwood_os_constants.cpp b/ravenwood/runtime-jni/ravenwood_os_constants.cpp
new file mode 100644
index 0000000..ea6c9d4
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_os_constants.cpp
@@ -0,0 +1,766 @@
+/*
+ * 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.
+ */
+
+// Copied from libcore/luni/src/main/native/android_system_OsConstants.cpp,
+// changes annotated with [ravenwood-change].
+
+#define LOG_TAG "OsConstants"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <net/if_arp.h>
+#include <linux/if_ether.h>
+
+// After the others because these are not necessarily self-contained in glibc.
+#include <linux/if_addr.h>
+#include <linux/rtnetlink.h>
+
+// Include linux socket constants for setting sockopts
+#include <linux/udp.h>
+
+#include <net/if.h> // After <sys/socket.h> to work around a Mac header file bug.
+
+// [ravenwood-change] always include it
+// #if defined(__BIONIC__)
+#include <linux/capability.h>
+// #endif
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
+
+// [ravenwood-change] -- can't access "Portability.h", so just inline it here.
+// #include "Portability.h"
+#include <byteswap.h>
+#include <sys/sendfile.h>
+#include <sys/statvfs.h>
+#include <netdb.h>
+#include <linux/vm_sockets.h>
+
+// For LOG_ALWAYS_FATAL_IF
+#include "utils/Log.h"
+
+
+static void initConstant(JNIEnv* env, jclass c, const char* fieldName, int value) {
+ jfieldID field = env->GetStaticFieldID(c, fieldName, "I");
+ env->SetStaticIntField(c, field, value);
+}
+
+static void OsConstants_initConstants(JNIEnv* env, jclass) {
+ // [ravenwood-change] -- the constants are in the outer class, but this JNI method is in the
+ // nested class, so we need to get the outer class here.
+ jclass c = env->FindClass("android/system/OsConstants");
+ LOG_ALWAYS_FATAL_IF(c == NULL, "Unable to find class android/system/OsConstants");
+
+ initConstant(env, c, "AF_INET", AF_INET);
+ initConstant(env, c, "AF_INET6", AF_INET6);
+ initConstant(env, c, "AF_PACKET", AF_PACKET);
+ initConstant(env, c, "AF_NETLINK", AF_NETLINK);
+ initConstant(env, c, "AF_UNIX", AF_UNIX);
+ initConstant(env, c, "AF_VSOCK", AF_VSOCK);
+ initConstant(env, c, "AF_UNSPEC", AF_UNSPEC);
+ initConstant(env, c, "AI_ADDRCONFIG", AI_ADDRCONFIG);
+ initConstant(env, c, "AI_ALL", AI_ALL);
+ initConstant(env, c, "AI_CANONNAME", AI_CANONNAME);
+ initConstant(env, c, "AI_NUMERICHOST", AI_NUMERICHOST);
+#if defined(AI_NUMERICSERV)
+ initConstant(env, c, "AI_NUMERICSERV", AI_NUMERICSERV);
+#endif
+ initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
+ initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
+ initConstant(env, c, "ARPHRD_ETHER", ARPHRD_ETHER);
+ initConstant(env, c, "VMADDR_PORT_ANY", VMADDR_PORT_ANY);
+ initConstant(env, c, "VMADDR_CID_ANY", VMADDR_CID_ANY);
+ initConstant(env, c, "VMADDR_CID_LOCAL", VMADDR_CID_LOCAL);
+ initConstant(env, c, "VMADDR_CID_HOST", VMADDR_CID_HOST);
+ initConstant(env, c, "ARPHRD_LOOPBACK", ARPHRD_LOOPBACK);
+#if defined(CAP_LAST_CAP)
+ initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
+ initConstant(env, c, "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE);
+ initConstant(env, c, "CAP_BLOCK_SUSPEND", CAP_BLOCK_SUSPEND);
+ initConstant(env, c, "CAP_CHOWN", CAP_CHOWN);
+ initConstant(env, c, "CAP_DAC_OVERRIDE", CAP_DAC_OVERRIDE);
+ initConstant(env, c, "CAP_DAC_READ_SEARCH", CAP_DAC_READ_SEARCH);
+ initConstant(env, c, "CAP_FOWNER", CAP_FOWNER);
+ initConstant(env, c, "CAP_FSETID", CAP_FSETID);
+ initConstant(env, c, "CAP_IPC_LOCK", CAP_IPC_LOCK);
+ initConstant(env, c, "CAP_IPC_OWNER", CAP_IPC_OWNER);
+ initConstant(env, c, "CAP_KILL", CAP_KILL);
+ initConstant(env, c, "CAP_LAST_CAP", CAP_LAST_CAP);
+ initConstant(env, c, "CAP_LEASE", CAP_LEASE);
+ initConstant(env, c, "CAP_LINUX_IMMUTABLE", CAP_LINUX_IMMUTABLE);
+ initConstant(env, c, "CAP_MAC_ADMIN", CAP_MAC_ADMIN);
+ initConstant(env, c, "CAP_MAC_OVERRIDE", CAP_MAC_OVERRIDE);
+ initConstant(env, c, "CAP_MKNOD", CAP_MKNOD);
+ initConstant(env, c, "CAP_NET_ADMIN", CAP_NET_ADMIN);
+ initConstant(env, c, "CAP_NET_BIND_SERVICE", CAP_NET_BIND_SERVICE);
+ initConstant(env, c, "CAP_NET_BROADCAST", CAP_NET_BROADCAST);
+ initConstant(env, c, "CAP_NET_RAW", CAP_NET_RAW);
+ initConstant(env, c, "CAP_SETFCAP", CAP_SETFCAP);
+ initConstant(env, c, "CAP_SETGID", CAP_SETGID);
+ initConstant(env, c, "CAP_SETPCAP", CAP_SETPCAP);
+ initConstant(env, c, "CAP_SETUID", CAP_SETUID);
+ initConstant(env, c, "CAP_SYS_ADMIN", CAP_SYS_ADMIN);
+ initConstant(env, c, "CAP_SYS_BOOT", CAP_SYS_BOOT);
+ initConstant(env, c, "CAP_SYS_CHROOT", CAP_SYS_CHROOT);
+ initConstant(env, c, "CAP_SYSLOG", CAP_SYSLOG);
+ initConstant(env, c, "CAP_SYS_MODULE", CAP_SYS_MODULE);
+ initConstant(env, c, "CAP_SYS_NICE", CAP_SYS_NICE);
+ initConstant(env, c, "CAP_SYS_PACCT", CAP_SYS_PACCT);
+ initConstant(env, c, "CAP_SYS_PTRACE", CAP_SYS_PTRACE);
+ initConstant(env, c, "CAP_SYS_RAWIO", CAP_SYS_RAWIO);
+ initConstant(env, c, "CAP_SYS_RESOURCE", CAP_SYS_RESOURCE);
+ initConstant(env, c, "CAP_SYS_TIME", CAP_SYS_TIME);
+ initConstant(env, c, "CAP_SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG);
+ initConstant(env, c, "CAP_WAKE_ALARM", CAP_WAKE_ALARM);
+#endif
+ initConstant(env, c, "E2BIG", E2BIG);
+ initConstant(env, c, "EACCES", EACCES);
+ initConstant(env, c, "EADDRINUSE", EADDRINUSE);
+ initConstant(env, c, "EADDRNOTAVAIL", EADDRNOTAVAIL);
+ initConstant(env, c, "EAFNOSUPPORT", EAFNOSUPPORT);
+ initConstant(env, c, "EAGAIN", EAGAIN);
+ initConstant(env, c, "EAI_AGAIN", EAI_AGAIN);
+ initConstant(env, c, "EAI_BADFLAGS", EAI_BADFLAGS);
+ initConstant(env, c, "EAI_FAIL", EAI_FAIL);
+ initConstant(env, c, "EAI_FAMILY", EAI_FAMILY);
+ initConstant(env, c, "EAI_MEMORY", EAI_MEMORY);
+ initConstant(env, c, "EAI_NODATA", EAI_NODATA);
+ initConstant(env, c, "EAI_NONAME", EAI_NONAME);
+#if defined(EAI_OVERFLOW)
+ initConstant(env, c, "EAI_OVERFLOW", EAI_OVERFLOW);
+#endif
+ initConstant(env, c, "EAI_SERVICE", EAI_SERVICE);
+ initConstant(env, c, "EAI_SOCKTYPE", EAI_SOCKTYPE);
+ initConstant(env, c, "EAI_SYSTEM", EAI_SYSTEM);
+ initConstant(env, c, "EALREADY", EALREADY);
+ initConstant(env, c, "EBADF", EBADF);
+ initConstant(env, c, "EBADMSG", EBADMSG);
+ initConstant(env, c, "EBUSY", EBUSY);
+ initConstant(env, c, "ECANCELED", ECANCELED);
+ initConstant(env, c, "ECHILD", ECHILD);
+ initConstant(env, c, "ECONNABORTED", ECONNABORTED);
+ initConstant(env, c, "ECONNREFUSED", ECONNREFUSED);
+ initConstant(env, c, "ECONNRESET", ECONNRESET);
+ initConstant(env, c, "EDEADLK", EDEADLK);
+ initConstant(env, c, "EDESTADDRREQ", EDESTADDRREQ);
+ initConstant(env, c, "EDOM", EDOM);
+ initConstant(env, c, "EDQUOT", EDQUOT);
+ initConstant(env, c, "EEXIST", EEXIST);
+ initConstant(env, c, "EFAULT", EFAULT);
+ initConstant(env, c, "EFBIG", EFBIG);
+ initConstant(env, c, "EHOSTUNREACH", EHOSTUNREACH);
+ initConstant(env, c, "EIDRM", EIDRM);
+ initConstant(env, c, "EILSEQ", EILSEQ);
+ initConstant(env, c, "EINPROGRESS", EINPROGRESS);
+ initConstant(env, c, "EINTR", EINTR);
+ initConstant(env, c, "EINVAL", EINVAL);
+ initConstant(env, c, "EIO", EIO);
+ initConstant(env, c, "EISCONN", EISCONN);
+ initConstant(env, c, "EISDIR", EISDIR);
+ initConstant(env, c, "ELOOP", ELOOP);
+ initConstant(env, c, "EMFILE", EMFILE);
+ initConstant(env, c, "EMLINK", EMLINK);
+ initConstant(env, c, "EMSGSIZE", EMSGSIZE);
+ initConstant(env, c, "EMULTIHOP", EMULTIHOP);
+ initConstant(env, c, "ENAMETOOLONG", ENAMETOOLONG);
+ initConstant(env, c, "ENETDOWN", ENETDOWN);
+ initConstant(env, c, "ENETRESET", ENETRESET);
+ initConstant(env, c, "ENETUNREACH", ENETUNREACH);
+ initConstant(env, c, "ENFILE", ENFILE);
+ initConstant(env, c, "ENOBUFS", ENOBUFS);
+ initConstant(env, c, "ENODATA", ENODATA);
+ initConstant(env, c, "ENODEV", ENODEV);
+ initConstant(env, c, "ENOENT", ENOENT);
+ initConstant(env, c, "ENOEXEC", ENOEXEC);
+ initConstant(env, c, "ENOLCK", ENOLCK);
+ initConstant(env, c, "ENOLINK", ENOLINK);
+ initConstant(env, c, "ENOMEM", ENOMEM);
+ initConstant(env, c, "ENOMSG", ENOMSG);
+ initConstant(env, c, "ENONET", ENONET);
+ initConstant(env, c, "ENOPROTOOPT", ENOPROTOOPT);
+ initConstant(env, c, "ENOSPC", ENOSPC);
+ initConstant(env, c, "ENOSR", ENOSR);
+ initConstant(env, c, "ENOSTR", ENOSTR);
+ initConstant(env, c, "ENOSYS", ENOSYS);
+ initConstant(env, c, "ENOTCONN", ENOTCONN);
+ initConstant(env, c, "ENOTDIR", ENOTDIR);
+ initConstant(env, c, "ENOTEMPTY", ENOTEMPTY);
+ initConstant(env, c, "ENOTSOCK", ENOTSOCK);
+ initConstant(env, c, "ENOTSUP", ENOTSUP);
+ initConstant(env, c, "ENOTTY", ENOTTY);
+ initConstant(env, c, "ENXIO", ENXIO);
+ initConstant(env, c, "EOPNOTSUPP", EOPNOTSUPP);
+ initConstant(env, c, "EOVERFLOW", EOVERFLOW);
+ initConstant(env, c, "EPERM", EPERM);
+ initConstant(env, c, "EPIPE", EPIPE);
+ initConstant(env, c, "EPROTO", EPROTO);
+ initConstant(env, c, "EPROTONOSUPPORT", EPROTONOSUPPORT);
+ initConstant(env, c, "EPROTOTYPE", EPROTOTYPE);
+ initConstant(env, c, "ERANGE", ERANGE);
+ initConstant(env, c, "EROFS", EROFS);
+ initConstant(env, c, "ESPIPE", ESPIPE);
+ initConstant(env, c, "ESRCH", ESRCH);
+ initConstant(env, c, "ESTALE", ESTALE);
+ initConstant(env, c, "ETH_P_ALL", ETH_P_ALL);
+ initConstant(env, c, "ETH_P_ARP", ETH_P_ARP);
+ initConstant(env, c, "ETH_P_IP", ETH_P_IP);
+ initConstant(env, c, "ETH_P_IPV6", ETH_P_IPV6);
+ initConstant(env, c, "ETIME", ETIME);
+ initConstant(env, c, "ETIMEDOUT", ETIMEDOUT);
+ initConstant(env, c, "ETXTBSY", ETXTBSY);
+ initConstant(env, c, "EUSERS", EUSERS);
+#if EWOULDBLOCK != EAGAIN
+#error EWOULDBLOCK != EAGAIN
+#endif
+ initConstant(env, c, "EXDEV", EXDEV);
+ initConstant(env, c, "EXIT_FAILURE", EXIT_FAILURE);
+ initConstant(env, c, "EXIT_SUCCESS", EXIT_SUCCESS);
+ initConstant(env, c, "FD_CLOEXEC", FD_CLOEXEC);
+ initConstant(env, c, "FIONREAD", FIONREAD);
+ initConstant(env, c, "F_DUPFD", F_DUPFD);
+ initConstant(env, c, "F_DUPFD_CLOEXEC", F_DUPFD_CLOEXEC);
+ initConstant(env, c, "F_GETFD", F_GETFD);
+ initConstant(env, c, "F_GETFL", F_GETFL);
+ initConstant(env, c, "F_GETLK", F_GETLK);
+#if defined(F_GETLK64)
+ initConstant(env, c, "F_GETLK64", F_GETLK64);
+#endif
+ initConstant(env, c, "F_GETOWN", F_GETOWN);
+ initConstant(env, c, "F_OK", F_OK);
+ initConstant(env, c, "F_RDLCK", F_RDLCK);
+ initConstant(env, c, "F_SETFD", F_SETFD);
+ initConstant(env, c, "F_SETFL", F_SETFL);
+ initConstant(env, c, "F_SETLK", F_SETLK);
+#if defined(F_SETLK64)
+ initConstant(env, c, "F_SETLK64", F_SETLK64);
+#endif
+ initConstant(env, c, "F_SETLKW", F_SETLKW);
+#if defined(F_SETLKW64)
+ initConstant(env, c, "F_SETLKW64", F_SETLKW64);
+#endif
+ initConstant(env, c, "F_SETOWN", F_SETOWN);
+ initConstant(env, c, "F_UNLCK", F_UNLCK);
+ initConstant(env, c, "F_WRLCK", F_WRLCK);
+ initConstant(env, c, "ICMP_ECHO", ICMP_ECHO);
+ initConstant(env, c, "ICMP_ECHOREPLY", ICMP_ECHOREPLY);
+ initConstant(env, c, "ICMP6_ECHO_REQUEST", ICMP6_ECHO_REQUEST);
+ initConstant(env, c, "ICMP6_ECHO_REPLY", ICMP6_ECHO_REPLY);
+#if defined(IFA_F_DADFAILED)
+ initConstant(env, c, "IFA_F_DADFAILED", IFA_F_DADFAILED);
+#endif
+#if defined(IFA_F_DEPRECATED)
+ initConstant(env, c, "IFA_F_DEPRECATED", IFA_F_DEPRECATED);
+#endif
+#if defined(IFA_F_HOMEADDRESS)
+ initConstant(env, c, "IFA_F_HOMEADDRESS", IFA_F_HOMEADDRESS);
+#endif
+#if defined(IFA_F_MANAGETEMPADDR)
+ initConstant(env, c, "IFA_F_MANAGETEMPADDR", IFA_F_MANAGETEMPADDR);
+#endif
+#if defined(IFA_F_NODAD)
+ initConstant(env, c, "IFA_F_NODAD", IFA_F_NODAD);
+#endif
+#if defined(IFA_F_NOPREFIXROUTE)
+ initConstant(env, c, "IFA_F_NOPREFIXROUTE", IFA_F_NOPREFIXROUTE);
+#endif
+#if defined(IFA_F_OPTIMISTIC)
+ initConstant(env, c, "IFA_F_OPTIMISTIC", IFA_F_OPTIMISTIC);
+#endif
+#if defined(IFA_F_PERMANENT)
+ initConstant(env, c, "IFA_F_PERMANENT", IFA_F_PERMANENT);
+#endif
+#if defined(IFA_F_SECONDARY)
+ initConstant(env, c, "IFA_F_SECONDARY", IFA_F_SECONDARY);
+#endif
+#if defined(IFA_F_TEMPORARY)
+ initConstant(env, c, "IFA_F_TEMPORARY", IFA_F_TEMPORARY);
+#endif
+#if defined(IFA_F_TENTATIVE)
+ initConstant(env, c, "IFA_F_TENTATIVE", IFA_F_TENTATIVE);
+#endif
+ initConstant(env, c, "IFF_ALLMULTI", IFF_ALLMULTI);
+#if defined(IFF_AUTOMEDIA)
+ initConstant(env, c, "IFF_AUTOMEDIA", IFF_AUTOMEDIA);
+#endif
+ initConstant(env, c, "IFF_BROADCAST", IFF_BROADCAST);
+ initConstant(env, c, "IFF_DEBUG", IFF_DEBUG);
+#if defined(IFF_DYNAMIC)
+ initConstant(env, c, "IFF_DYNAMIC", IFF_DYNAMIC);
+#endif
+ initConstant(env, c, "IFF_LOOPBACK", IFF_LOOPBACK);
+#if defined(IFF_MASTER)
+ initConstant(env, c, "IFF_MASTER", IFF_MASTER);
+#endif
+ initConstant(env, c, "IFF_MULTICAST", IFF_MULTICAST);
+ initConstant(env, c, "IFF_NOARP", IFF_NOARP);
+ initConstant(env, c, "IFF_NOTRAILERS", IFF_NOTRAILERS);
+ initConstant(env, c, "IFF_POINTOPOINT", IFF_POINTOPOINT);
+#if defined(IFF_PORTSEL)
+ initConstant(env, c, "IFF_PORTSEL", IFF_PORTSEL);
+#endif
+ initConstant(env, c, "IFF_PROMISC", IFF_PROMISC);
+ initConstant(env, c, "IFF_RUNNING", IFF_RUNNING);
+#if defined(IFF_SLAVE)
+ initConstant(env, c, "IFF_SLAVE", IFF_SLAVE);
+#endif
+ initConstant(env, c, "IFF_UP", IFF_UP);
+ initConstant(env, c, "IPPROTO_ICMP", IPPROTO_ICMP);
+ initConstant(env, c, "IPPROTO_ICMPV6", IPPROTO_ICMPV6);
+ initConstant(env, c, "IPPROTO_IP", IPPROTO_IP);
+ initConstant(env, c, "IPPROTO_IPV6", IPPROTO_IPV6);
+ initConstant(env, c, "IPPROTO_RAW", IPPROTO_RAW);
+ initConstant(env, c, "IPPROTO_TCP", IPPROTO_TCP);
+ initConstant(env, c, "IPPROTO_UDP", IPPROTO_UDP);
+ initConstant(env, c, "IPPROTO_ESP", IPPROTO_ESP);
+ initConstant(env, c, "IPV6_CHECKSUM", IPV6_CHECKSUM);
+ initConstant(env, c, "IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS);
+ initConstant(env, c, "IPV6_MULTICAST_IF", IPV6_MULTICAST_IF);
+ initConstant(env, c, "IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP);
+#if defined(IPV6_PKTINFO)
+ initConstant(env, c, "IPV6_PKTINFO", IPV6_PKTINFO);
+#endif
+#if defined(IPV6_RECVDSTOPTS)
+ initConstant(env, c, "IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS);
+#endif
+#if defined(IPV6_RECVHOPLIMIT)
+ initConstant(env, c, "IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT);
+#endif
+#if defined(IPV6_RECVHOPOPTS)
+ initConstant(env, c, "IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS);
+#endif
+#if defined(IPV6_RECVPKTINFO)
+ initConstant(env, c, "IPV6_RECVPKTINFO", IPV6_RECVPKTINFO);
+#endif
+#if defined(IPV6_RECVRTHDR)
+ initConstant(env, c, "IPV6_RECVRTHDR", IPV6_RECVRTHDR);
+#endif
+#if defined(IPV6_RECVTCLASS)
+ initConstant(env, c, "IPV6_RECVTCLASS", IPV6_RECVTCLASS);
+#endif
+#if defined(IPV6_TCLASS)
+ initConstant(env, c, "IPV6_TCLASS", IPV6_TCLASS);
+#endif
+ initConstant(env, c, "IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS);
+ initConstant(env, c, "IPV6_V6ONLY", IPV6_V6ONLY);
+ initConstant(env, c, "IP_MULTICAST_ALL", IP_MULTICAST_ALL);
+ initConstant(env, c, "IP_MULTICAST_IF", IP_MULTICAST_IF);
+ initConstant(env, c, "IP_MULTICAST_LOOP", IP_MULTICAST_LOOP);
+ initConstant(env, c, "IP_MULTICAST_TTL", IP_MULTICAST_TTL);
+ initConstant(env, c, "IP_RECVTOS", IP_RECVTOS);
+ initConstant(env, c, "IP_TOS", IP_TOS);
+ initConstant(env, c, "IP_TTL", IP_TTL);
+#if defined(_LINUX_CAPABILITY_VERSION_3)
+ initConstant(env, c, "_LINUX_CAPABILITY_VERSION_3", _LINUX_CAPABILITY_VERSION_3);
+#endif
+ initConstant(env, c, "MAP_FIXED", MAP_FIXED);
+ initConstant(env, c, "MAP_ANONYMOUS", MAP_ANONYMOUS);
+ initConstant(env, c, "MAP_POPULATE", MAP_POPULATE);
+ initConstant(env, c, "MAP_PRIVATE", MAP_PRIVATE);
+ initConstant(env, c, "MAP_SHARED", MAP_SHARED);
+#if defined(MCAST_JOIN_GROUP)
+ initConstant(env, c, "MCAST_JOIN_GROUP", MCAST_JOIN_GROUP);
+#endif
+#if defined(MCAST_LEAVE_GROUP)
+ initConstant(env, c, "MCAST_LEAVE_GROUP", MCAST_LEAVE_GROUP);
+#endif
+#if defined(MCAST_JOIN_SOURCE_GROUP)
+ initConstant(env, c, "MCAST_JOIN_SOURCE_GROUP", MCAST_JOIN_SOURCE_GROUP);
+#endif
+#if defined(MCAST_LEAVE_SOURCE_GROUP)
+ initConstant(env, c, "MCAST_LEAVE_SOURCE_GROUP", MCAST_LEAVE_SOURCE_GROUP);
+#endif
+#if defined(MCAST_BLOCK_SOURCE)
+ initConstant(env, c, "MCAST_BLOCK_SOURCE", MCAST_BLOCK_SOURCE);
+#endif
+#if defined(MCAST_UNBLOCK_SOURCE)
+ initConstant(env, c, "MCAST_UNBLOCK_SOURCE", MCAST_UNBLOCK_SOURCE);
+#endif
+ initConstant(env, c, "MCL_CURRENT", MCL_CURRENT);
+ initConstant(env, c, "MCL_FUTURE", MCL_FUTURE);
+#if defined(MFD_CLOEXEC)
+ initConstant(env, c, "MFD_CLOEXEC", MFD_CLOEXEC);
+#endif
+ initConstant(env, c, "MSG_CTRUNC", MSG_CTRUNC);
+ initConstant(env, c, "MSG_DONTROUTE", MSG_DONTROUTE);
+ initConstant(env, c, "MSG_EOR", MSG_EOR);
+ initConstant(env, c, "MSG_OOB", MSG_OOB);
+ initConstant(env, c, "MSG_PEEK", MSG_PEEK);
+ initConstant(env, c, "MSG_TRUNC", MSG_TRUNC);
+ initConstant(env, c, "MSG_WAITALL", MSG_WAITALL);
+ initConstant(env, c, "MS_ASYNC", MS_ASYNC);
+ initConstant(env, c, "MS_INVALIDATE", MS_INVALIDATE);
+ initConstant(env, c, "MS_SYNC", MS_SYNC);
+ initConstant(env, c, "NETLINK_NETFILTER", NETLINK_NETFILTER);
+ initConstant(env, c, "NETLINK_ROUTE", NETLINK_ROUTE);
+ initConstant(env, c, "NETLINK_INET_DIAG", NETLINK_INET_DIAG);
+ initConstant(env, c, "NETLINK_XFRM", NETLINK_XFRM);
+ initConstant(env, c, "NI_DGRAM", NI_DGRAM);
+ initConstant(env, c, "NI_NAMEREQD", NI_NAMEREQD);
+ initConstant(env, c, "NI_NOFQDN", NI_NOFQDN);
+ initConstant(env, c, "NI_NUMERICHOST", NI_NUMERICHOST);
+ initConstant(env, c, "NI_NUMERICSERV", NI_NUMERICSERV);
+ initConstant(env, c, "O_ACCMODE", O_ACCMODE);
+ initConstant(env, c, "O_APPEND", O_APPEND);
+ initConstant(env, c, "O_CLOEXEC", O_CLOEXEC);
+ initConstant(env, c, "O_CREAT", O_CREAT);
+ initConstant(env, c, "O_DIRECT", O_DIRECT);
+ initConstant(env, c, "O_EXCL", O_EXCL);
+ initConstant(env, c, "O_NOCTTY", O_NOCTTY);
+ initConstant(env, c, "O_NOFOLLOW", O_NOFOLLOW);
+ initConstant(env, c, "O_NONBLOCK", O_NONBLOCK);
+ initConstant(env, c, "O_RDONLY", O_RDONLY);
+ initConstant(env, c, "O_RDWR", O_RDWR);
+ initConstant(env, c, "O_SYNC", O_SYNC);
+ initConstant(env, c, "O_DSYNC", O_DSYNC);
+ initConstant(env, c, "O_TRUNC", O_TRUNC);
+ initConstant(env, c, "O_WRONLY", O_WRONLY);
+ initConstant(env, c, "POLLERR", POLLERR);
+ initConstant(env, c, "POLLHUP", POLLHUP);
+ initConstant(env, c, "POLLIN", POLLIN);
+ initConstant(env, c, "POLLNVAL", POLLNVAL);
+ initConstant(env, c, "POLLOUT", POLLOUT);
+ initConstant(env, c, "POLLPRI", POLLPRI);
+ initConstant(env, c, "POLLRDBAND", POLLRDBAND);
+ initConstant(env, c, "POLLRDNORM", POLLRDNORM);
+ initConstant(env, c, "POLLWRBAND", POLLWRBAND);
+ initConstant(env, c, "POLLWRNORM", POLLWRNORM);
+#if defined(PR_CAP_AMBIENT)
+ initConstant(env, c, "PR_CAP_AMBIENT", PR_CAP_AMBIENT);
+#endif
+#if defined(PR_CAP_AMBIENT_RAISE)
+ initConstant(env, c, "PR_CAP_AMBIENT_RAISE", PR_CAP_AMBIENT_RAISE);
+#endif
+#if defined(PR_GET_DUMPABLE)
+ initConstant(env, c, "PR_GET_DUMPABLE", PR_GET_DUMPABLE);
+#endif
+#if defined(PR_SET_DUMPABLE)
+ initConstant(env, c, "PR_SET_DUMPABLE", PR_SET_DUMPABLE);
+#endif
+#if defined(PR_SET_NO_NEW_PRIVS)
+ initConstant(env, c, "PR_SET_NO_NEW_PRIVS", PR_SET_NO_NEW_PRIVS);
+#endif
+ initConstant(env, c, "PROT_EXEC", PROT_EXEC);
+ initConstant(env, c, "PROT_NONE", PROT_NONE);
+ initConstant(env, c, "PROT_READ", PROT_READ);
+ initConstant(env, c, "PROT_WRITE", PROT_WRITE);
+ initConstant(env, c, "R_OK", R_OK);
+ initConstant(env, c, "RLIMIT_NOFILE", RLIMIT_NOFILE);
+// NOTE: The RT_* constants are not preprocessor defines, they're enum
+// members. The best we can do (barring UAPI / kernel version checks) is
+// to hope they exist on all host linuxes we're building on. These
+// constants have been around since 2.6.35 at least, so we should be ok.
+ initConstant(env, c, "RT_SCOPE_HOST", RT_SCOPE_HOST);
+ initConstant(env, c, "RT_SCOPE_LINK", RT_SCOPE_LINK);
+ initConstant(env, c, "RT_SCOPE_NOWHERE", RT_SCOPE_NOWHERE);
+ initConstant(env, c, "RT_SCOPE_SITE", RT_SCOPE_SITE);
+ initConstant(env, c, "RT_SCOPE_UNIVERSE", RT_SCOPE_UNIVERSE);
+ initConstant(env, c, "RTMGRP_IPV4_IFADDR", RTMGRP_IPV4_IFADDR);
+ initConstant(env, c, "RTMGRP_IPV4_MROUTE", RTMGRP_IPV4_MROUTE);
+ initConstant(env, c, "RTMGRP_IPV4_ROUTE", RTMGRP_IPV4_ROUTE);
+ initConstant(env, c, "RTMGRP_IPV4_RULE", RTMGRP_IPV4_RULE);
+ initConstant(env, c, "RTMGRP_IPV6_IFADDR", RTMGRP_IPV6_IFADDR);
+ initConstant(env, c, "RTMGRP_IPV6_IFINFO", RTMGRP_IPV6_IFINFO);
+ initConstant(env, c, "RTMGRP_IPV6_MROUTE", RTMGRP_IPV6_MROUTE);
+ initConstant(env, c, "RTMGRP_IPV6_PREFIX", RTMGRP_IPV6_PREFIX);
+ initConstant(env, c, "RTMGRP_IPV6_ROUTE", RTMGRP_IPV6_ROUTE);
+ initConstant(env, c, "RTMGRP_LINK", RTMGRP_LINK);
+ initConstant(env, c, "RTMGRP_NEIGH", RTMGRP_NEIGH);
+ initConstant(env, c, "RTMGRP_NOTIFY", RTMGRP_NOTIFY);
+ initConstant(env, c, "RTMGRP_TC", RTMGRP_TC);
+ initConstant(env, c, "SEEK_CUR", SEEK_CUR);
+ initConstant(env, c, "SEEK_END", SEEK_END);
+ initConstant(env, c, "SEEK_SET", SEEK_SET);
+ initConstant(env, c, "SHUT_RD", SHUT_RD);
+ initConstant(env, c, "SHUT_RDWR", SHUT_RDWR);
+ initConstant(env, c, "SHUT_WR", SHUT_WR);
+ initConstant(env, c, "SIGABRT", SIGABRT);
+ initConstant(env, c, "SIGALRM", SIGALRM);
+ initConstant(env, c, "SIGBUS", SIGBUS);
+ initConstant(env, c, "SIGCHLD", SIGCHLD);
+ initConstant(env, c, "SIGCONT", SIGCONT);
+ initConstant(env, c, "SIGFPE", SIGFPE);
+ initConstant(env, c, "SIGHUP", SIGHUP);
+ initConstant(env, c, "SIGILL", SIGILL);
+ initConstant(env, c, "SIGINT", SIGINT);
+ initConstant(env, c, "SIGIO", SIGIO);
+ initConstant(env, c, "SIGKILL", SIGKILL);
+ initConstant(env, c, "SIGPIPE", SIGPIPE);
+ initConstant(env, c, "SIGPROF", SIGPROF);
+#if defined(SIGPWR)
+ initConstant(env, c, "SIGPWR", SIGPWR);
+#endif
+ initConstant(env, c, "SIGQUIT", SIGQUIT);
+#if defined(SIGRTMAX)
+ initConstant(env, c, "SIGRTMAX", SIGRTMAX);
+#endif
+#if defined(SIGRTMIN)
+ initConstant(env, c, "SIGRTMIN", SIGRTMIN);
+#endif
+ initConstant(env, c, "SIGSEGV", SIGSEGV);
+#if defined(SIGSTKFLT)
+ initConstant(env, c, "SIGSTKFLT", SIGSTKFLT);
+#endif
+ initConstant(env, c, "SIGSTOP", SIGSTOP);
+ initConstant(env, c, "SIGSYS", SIGSYS);
+ initConstant(env, c, "SIGTERM", SIGTERM);
+ initConstant(env, c, "SIGTRAP", SIGTRAP);
+ initConstant(env, c, "SIGTSTP", SIGTSTP);
+ initConstant(env, c, "SIGTTIN", SIGTTIN);
+ initConstant(env, c, "SIGTTOU", SIGTTOU);
+ initConstant(env, c, "SIGURG", SIGURG);
+ initConstant(env, c, "SIGUSR1", SIGUSR1);
+ initConstant(env, c, "SIGUSR2", SIGUSR2);
+ initConstant(env, c, "SIGVTALRM", SIGVTALRM);
+ initConstant(env, c, "SIGWINCH", SIGWINCH);
+ initConstant(env, c, "SIGXCPU", SIGXCPU);
+ initConstant(env, c, "SIGXFSZ", SIGXFSZ);
+ initConstant(env, c, "SIOCGIFADDR", SIOCGIFADDR);
+ initConstant(env, c, "SIOCGIFBRDADDR", SIOCGIFBRDADDR);
+ initConstant(env, c, "SIOCGIFDSTADDR", SIOCGIFDSTADDR);
+ initConstant(env, c, "SIOCGIFNETMASK", SIOCGIFNETMASK);
+ initConstant(env, c, "SOCK_CLOEXEC", SOCK_CLOEXEC);
+ initConstant(env, c, "SOCK_DGRAM", SOCK_DGRAM);
+ initConstant(env, c, "SOCK_NONBLOCK", SOCK_NONBLOCK);
+ initConstant(env, c, "SOCK_RAW", SOCK_RAW);
+ initConstant(env, c, "SOCK_SEQPACKET", SOCK_SEQPACKET);
+ initConstant(env, c, "SOCK_STREAM", SOCK_STREAM);
+ initConstant(env, c, "SOL_SOCKET", SOL_SOCKET);
+#if defined(SOL_UDP)
+ initConstant(env, c, "SOL_UDP", SOL_UDP);
+#endif
+ initConstant(env, c, "SOL_PACKET", SOL_PACKET);
+#if defined(SO_BINDTODEVICE)
+ initConstant(env, c, "SO_BINDTODEVICE", SO_BINDTODEVICE);
+#endif
+ initConstant(env, c, "SO_BROADCAST", SO_BROADCAST);
+ initConstant(env, c, "SO_DEBUG", SO_DEBUG);
+#if defined(SO_DOMAIN)
+ initConstant(env, c, "SO_DOMAIN", SO_DOMAIN);
+#endif
+ initConstant(env, c, "SO_DONTROUTE", SO_DONTROUTE);
+ initConstant(env, c, "SO_ERROR", SO_ERROR);
+ initConstant(env, c, "SO_KEEPALIVE", SO_KEEPALIVE);
+ initConstant(env, c, "SO_LINGER", SO_LINGER);
+ initConstant(env, c, "SO_OOBINLINE", SO_OOBINLINE);
+#if defined(SO_PASSCRED)
+ initConstant(env, c, "SO_PASSCRED", SO_PASSCRED);
+#endif
+#if defined(SO_PEERCRED)
+ initConstant(env, c, "SO_PEERCRED", SO_PEERCRED);
+#endif
+#if defined(SO_PROTOCOL)
+ initConstant(env, c, "SO_PROTOCOL", SO_PROTOCOL);
+#endif
+ initConstant(env, c, "SO_RCVBUF", SO_RCVBUF);
+ initConstant(env, c, "SO_RCVLOWAT", SO_RCVLOWAT);
+ initConstant(env, c, "SO_RCVTIMEO", SO_RCVTIMEO);
+ initConstant(env, c, "SO_REUSEADDR", SO_REUSEADDR);
+ initConstant(env, c, "SO_SNDBUF", SO_SNDBUF);
+ initConstant(env, c, "SO_SNDLOWAT", SO_SNDLOWAT);
+ initConstant(env, c, "SO_SNDTIMEO", SO_SNDTIMEO);
+ initConstant(env, c, "SO_TYPE", SO_TYPE);
+#if defined(PACKET_IGNORE_OUTGOING)
+ initConstant(env, c, "PACKET_IGNORE_OUTGOING", PACKET_IGNORE_OUTGOING);
+#endif
+ initConstant(env, c, "SPLICE_F_MOVE", SPLICE_F_MOVE);
+ initConstant(env, c, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK);
+ initConstant(env, c, "SPLICE_F_MORE", SPLICE_F_MORE);
+ initConstant(env, c, "STDERR_FILENO", STDERR_FILENO);
+ initConstant(env, c, "STDIN_FILENO", STDIN_FILENO);
+ initConstant(env, c, "STDOUT_FILENO", STDOUT_FILENO);
+ initConstant(env, c, "ST_MANDLOCK", ST_MANDLOCK);
+ initConstant(env, c, "ST_NOATIME", ST_NOATIME);
+ initConstant(env, c, "ST_NODEV", ST_NODEV);
+ initConstant(env, c, "ST_NODIRATIME", ST_NODIRATIME);
+ initConstant(env, c, "ST_NOEXEC", ST_NOEXEC);
+ initConstant(env, c, "ST_NOSUID", ST_NOSUID);
+ initConstant(env, c, "ST_RDONLY", ST_RDONLY);
+ initConstant(env, c, "ST_RELATIME", ST_RELATIME);
+ initConstant(env, c, "ST_SYNCHRONOUS", ST_SYNCHRONOUS);
+ initConstant(env, c, "S_IFBLK", S_IFBLK);
+ initConstant(env, c, "S_IFCHR", S_IFCHR);
+ initConstant(env, c, "S_IFDIR", S_IFDIR);
+ initConstant(env, c, "S_IFIFO", S_IFIFO);
+ initConstant(env, c, "S_IFLNK", S_IFLNK);
+ initConstant(env, c, "S_IFMT", S_IFMT);
+ initConstant(env, c, "S_IFREG", S_IFREG);
+ initConstant(env, c, "S_IFSOCK", S_IFSOCK);
+ initConstant(env, c, "S_IRGRP", S_IRGRP);
+ initConstant(env, c, "S_IROTH", S_IROTH);
+ initConstant(env, c, "S_IRUSR", S_IRUSR);
+ initConstant(env, c, "S_IRWXG", S_IRWXG);
+ initConstant(env, c, "S_IRWXO", S_IRWXO);
+ initConstant(env, c, "S_IRWXU", S_IRWXU);
+ initConstant(env, c, "S_ISGID", S_ISGID);
+ initConstant(env, c, "S_ISUID", S_ISUID);
+ initConstant(env, c, "S_ISVTX", S_ISVTX);
+ initConstant(env, c, "S_IWGRP", S_IWGRP);
+ initConstant(env, c, "S_IWOTH", S_IWOTH);
+ initConstant(env, c, "S_IWUSR", S_IWUSR);
+ initConstant(env, c, "S_IXGRP", S_IXGRP);
+ initConstant(env, c, "S_IXOTH", S_IXOTH);
+ initConstant(env, c, "S_IXUSR", S_IXUSR);
+ initConstant(env, c, "TCP_NODELAY", TCP_NODELAY);
+#if defined(TCP_USER_TIMEOUT)
+ initConstant(env, c, "TCP_USER_TIMEOUT", TCP_USER_TIMEOUT);
+#endif
+ initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
+ initConstant(env, c, "UDP_ENCAP", UDP_ENCAP);
+ initConstant(env, c, "UDP_ENCAP_ESPINUDP_NON_IKE", UDP_ENCAP_ESPINUDP_NON_IKE);
+ initConstant(env, c, "UDP_ENCAP_ESPINUDP", UDP_ENCAP_ESPINUDP);
+#if defined(UDP_GRO)
+ initConstant(env, c, "UDP_GRO", UDP_GRO);
+#endif
+#if defined(UDP_SEGMENT)
+ initConstant(env, c, "UDP_SEGMENT", UDP_SEGMENT);
+#endif
+ // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
+ initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
+ initConstant(env, c, "WCONTINUED", WCONTINUED);
+ initConstant(env, c, "WEXITED", WEXITED);
+ initConstant(env, c, "WNOHANG", WNOHANG);
+ initConstant(env, c, "WNOWAIT", WNOWAIT);
+ initConstant(env, c, "WSTOPPED", WSTOPPED);
+ initConstant(env, c, "WUNTRACED", WUNTRACED);
+ initConstant(env, c, "W_OK", W_OK);
+ initConstant(env, c, "XATTR_CREATE", XATTR_CREATE);
+ initConstant(env, c, "XATTR_REPLACE", XATTR_REPLACE);
+ initConstant(env, c, "X_OK", X_OK);
+ initConstant(env, c, "_SC_2_CHAR_TERM", _SC_2_CHAR_TERM);
+ initConstant(env, c, "_SC_2_C_BIND", _SC_2_C_BIND);
+ initConstant(env, c, "_SC_2_C_DEV", _SC_2_C_DEV);
+#if defined(_SC_2_C_VERSION)
+ initConstant(env, c, "_SC_2_C_VERSION", _SC_2_C_VERSION);
+#endif
+ initConstant(env, c, "_SC_2_FORT_DEV", _SC_2_FORT_DEV);
+ initConstant(env, c, "_SC_2_FORT_RUN", _SC_2_FORT_RUN);
+ initConstant(env, c, "_SC_2_LOCALEDEF", _SC_2_LOCALEDEF);
+ initConstant(env, c, "_SC_2_SW_DEV", _SC_2_SW_DEV);
+ initConstant(env, c, "_SC_2_UPE", _SC_2_UPE);
+ initConstant(env, c, "_SC_2_VERSION", _SC_2_VERSION);
+ initConstant(env, c, "_SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX);
+ initConstant(env, c, "_SC_AIO_MAX", _SC_AIO_MAX);
+ initConstant(env, c, "_SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX);
+ initConstant(env, c, "_SC_ARG_MAX", _SC_ARG_MAX);
+ initConstant(env, c, "_SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO);
+ initConstant(env, c, "_SC_ATEXIT_MAX", _SC_ATEXIT_MAX);
+#if defined(_SC_AVPHYS_PAGES)
+ initConstant(env, c, "_SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES);
+#endif
+ initConstant(env, c, "_SC_BC_BASE_MAX", _SC_BC_BASE_MAX);
+ initConstant(env, c, "_SC_BC_DIM_MAX", _SC_BC_DIM_MAX);
+ initConstant(env, c, "_SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX);
+ initConstant(env, c, "_SC_BC_STRING_MAX", _SC_BC_STRING_MAX);
+ initConstant(env, c, "_SC_CHILD_MAX", _SC_CHILD_MAX);
+ initConstant(env, c, "_SC_CLK_TCK", _SC_CLK_TCK);
+ initConstant(env, c, "_SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX);
+ initConstant(env, c, "_SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX);
+ initConstant(env, c, "_SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX);
+ initConstant(env, c, "_SC_FSYNC", _SC_FSYNC);
+ initConstant(env, c, "_SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX);
+ initConstant(env, c, "_SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX);
+ initConstant(env, c, "_SC_IOV_MAX", _SC_IOV_MAX);
+ initConstant(env, c, "_SC_JOB_CONTROL", _SC_JOB_CONTROL);
+ initConstant(env, c, "_SC_LINE_MAX", _SC_LINE_MAX);
+ initConstant(env, c, "_SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX);
+ initConstant(env, c, "_SC_MAPPED_FILES", _SC_MAPPED_FILES);
+ initConstant(env, c, "_SC_MEMLOCK", _SC_MEMLOCK);
+ initConstant(env, c, "_SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE);
+ initConstant(env, c, "_SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION);
+ initConstant(env, c, "_SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING);
+ initConstant(env, c, "_SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX);
+ initConstant(env, c, "_SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX);
+ initConstant(env, c, "_SC_NGROUPS_MAX", _SC_NGROUPS_MAX);
+ initConstant(env, c, "_SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF);
+ initConstant(env, c, "_SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN);
+ initConstant(env, c, "_SC_OPEN_MAX", _SC_OPEN_MAX);
+ initConstant(env, c, "_SC_PAGESIZE", _SC_PAGESIZE);
+ initConstant(env, c, "_SC_PAGE_SIZE", _SC_PAGE_SIZE);
+ initConstant(env, c, "_SC_PASS_MAX", _SC_PASS_MAX);
+#if defined(_SC_PHYS_PAGES)
+ initConstant(env, c, "_SC_PHYS_PAGES", _SC_PHYS_PAGES);
+#endif
+ initConstant(env, c, "_SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO);
+ initConstant(env, c, "_SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING);
+ initConstant(env, c, "_SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS);
+ initConstant(env, c, "_SC_RE_DUP_MAX", _SC_RE_DUP_MAX);
+ initConstant(env, c, "_SC_RTSIG_MAX", _SC_RTSIG_MAX);
+ initConstant(env, c, "_SC_SAVED_IDS", _SC_SAVED_IDS);
+ initConstant(env, c, "_SC_SEMAPHORES", _SC_SEMAPHORES);
+ initConstant(env, c, "_SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX);
+ initConstant(env, c, "_SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX);
+ initConstant(env, c, "_SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS);
+ initConstant(env, c, "_SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX);
+ initConstant(env, c, "_SC_STREAM_MAX", _SC_STREAM_MAX);
+ initConstant(env, c, "_SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO);
+ initConstant(env, c, "_SC_THREADS", _SC_THREADS);
+ initConstant(env, c, "_SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR);
+ initConstant(env, c, "_SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE);
+ initConstant(env, c, "_SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS);
+ initConstant(env, c, "_SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX);
+ initConstant(env, c, "_SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING);
+ initConstant(env, c, "_SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT);
+ initConstant(env, c, "_SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT);
+ initConstant(env, c, "_SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS);
+ initConstant(env, c, "_SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN);
+ initConstant(env, c, "_SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX);
+ initConstant(env, c, "_SC_TIMERS", _SC_TIMERS);
+ initConstant(env, c, "_SC_TIMER_MAX", _SC_TIMER_MAX);
+ initConstant(env, c, "_SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX);
+ initConstant(env, c, "_SC_TZNAME_MAX", _SC_TZNAME_MAX);
+ initConstant(env, c, "_SC_VERSION", _SC_VERSION);
+ initConstant(env, c, "_SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32);
+ initConstant(env, c, "_SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG);
+ initConstant(env, c, "_SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64);
+ initConstant(env, c, "_SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG);
+ initConstant(env, c, "_SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT);
+ initConstant(env, c, "_SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N);
+ initConstant(env, c, "_SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY);
+ initConstant(env, c, "_SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME);
+ initConstant(env, c, "_SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS);
+ initConstant(env, c, "_SC_XOPEN_SHM", _SC_XOPEN_SHM);
+ initConstant(env, c, "_SC_XOPEN_UNIX", _SC_XOPEN_UNIX);
+ initConstant(env, c, "_SC_XOPEN_VERSION", _SC_XOPEN_VERSION);
+ initConstant(env, c, "_SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(OsConstants, initConstants, "()V"),
+};
+
+void register_android_system_OsConstants(JNIEnv* env) {
+ // [ravenwood-change] -- method moved to the nested class
+ jniRegisterNativeMethods(env, "android/system/OsConstants$Native", gMethods, NELEM(gMethods));
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
new file mode 100644
index 0000000..34cf9f9
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+// Defined in ravenwood_os_constants.cpp
+void register_android_system_OsConstants(JNIEnv* env);
+
+// ---- Exception related ----
+
+static void throwErrnoException(JNIEnv* env, const char* functionName) {
+ int error = errno;
+ jniThrowErrnoException(env, functionName, error);
+}
+
+template <typename rc_t>
+static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) {
+ if (rc == rc_t(-1)) {
+ throwErrnoException(env, name);
+ }
+ return rc;
+}
+
+// ---- JNI methods ----
+
+typedef void (*FreeFunction)(void*);
+
+static void nApplyFreeFunction(JNIEnv*, jclass, jlong freeFunction, jlong ptr) {
+ void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+ FreeFunction nativeFreeFunction
+ = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+ nativeFreeFunction(nativePtr);
+}
+
+static jint nFcntlInt(JNIEnv* env, jclass, jint fd, jint cmd, jint arg) {
+ return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
+}
+
+static jlong nLseek(JNIEnv* env, jclass, jint fd, jlong offset, jint whence) {
+ return throwIfMinusOne(env, "lseek", TEMP_FAILURE_RETRY(lseek(fd, offset, whence)));
+}
+
+static jintArray nPipe2(JNIEnv* env, jclass, jint flags) {
+ int fds[2];
+ throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(fds, flags)));
+
+ jintArray result;
+ result = env->NewIntArray(2);
+ if (result == NULL) {
+ return NULL; /* out of memory error thrown */
+ }
+ env->SetIntArrayRegion(result, 0, 2, fds);
+ return result;
+}
+
+static jlong nDup(JNIEnv* env, jclass, jint fd) {
+ return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, F_DUPFD_CLOEXEC, 0)));
+}
+
+// ---- Registration ----
+
+static const JNINativeMethod sMethods[] =
+{
+ { "applyFreeFunction", "(JJ)V", (void*)nApplyFreeFunction },
+ { "nFcntlInt", "(III)I", (void*)nFcntlInt },
+ { "nLseek", "(IJI)J", (void*)nLseek },
+ { "nPipe2", "(I)[I", (void*)nPipe2 },
+ { "nDup", "(I)I", (void*)nDup },
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+
+ ALOGI("%s: JNI_OnLoad", __FILE__);
+
+ jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/common/RavenwoodRuntimeNative",
+ sMethods, NELEM(sMethods));
+ if (res < 0) {
+ return res;
+ }
+
+ register_android_system_OsConstants(env);
+
+ return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/runtime-test/Android.bp
new file mode 100644
index 0000000..4102920
--- /dev/null
+++ b/ravenwood/runtime-test/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodRuntimeTest",
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ ],
+ srcs: [
+ "test/**/*.java",
+ ],
+ // sdk_version: "module_current",
+ auto_gen_config: true,
+}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
new file mode 100644
index 0000000..3332e24
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
@@ -0,0 +1,444 @@
+/*
+ * 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.ravenwood.runtimetest;
+
+// Copied from libcore/luni/src/test/java/libcore/android/system/OsConstantsTest.java
+
+import static android.system.OsConstants.CAP_TO_INDEX;
+import static android.system.OsConstants.CAP_TO_MASK;
+import static android.system.OsConstants.S_ISBLK;
+import static android.system.OsConstants.S_ISCHR;
+import static android.system.OsConstants.S_ISDIR;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.S_ISSOCK;
+import static android.system.OsConstants.WCOREDUMP;
+import static android.system.OsConstants.WEXITSTATUS;
+import static android.system.OsConstants.WIFEXITED;
+import static android.system.OsConstants.WIFSIGNALED;
+import static android.system.OsConstants.WIFSTOPPED;
+import static android.system.OsConstants.WSTOPSIG;
+import static android.system.OsConstants.WTERMSIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.system.OsConstants;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class OsConstantsTest {
+
+ // http://b/15602893
+ @Test
+ public void testBug15602893() {
+ assertTrue(OsConstants.RT_SCOPE_HOST > 0);
+ assertTrue(OsConstants.RT_SCOPE_LINK > 0);
+ assertTrue(OsConstants.RT_SCOPE_SITE > 0);
+
+ assertTrue(OsConstants.IFA_F_TENTATIVE > 0);
+ }
+
+ // introduced for http://b/30402085
+ @Test
+ public void testTcpUserTimeoutIsDefined() {
+ assertTrue(OsConstants.TCP_USER_TIMEOUT > 0);
+ }
+
+ /**
+ * Verifies equality assertions given in the documentation for
+ * {@link OsConstants#SOCK_CLOEXEC} and {@link OsConstants#SOCK_NONBLOCK}.
+ */
+ @Test
+ public void testConstantsEqual() {
+ assertEquals(OsConstants.O_CLOEXEC, OsConstants.SOCK_CLOEXEC);
+ assertEquals(OsConstants.O_NONBLOCK, OsConstants.SOCK_NONBLOCK);
+ }
+
+ @Test
+ public void test_CAP_constants() {
+ assertEquals(0, OsConstants.CAP_CHOWN);
+ assertEquals(1, OsConstants.CAP_DAC_OVERRIDE);
+ assertEquals(2, OsConstants.CAP_DAC_READ_SEARCH);
+ assertEquals(3, OsConstants.CAP_FOWNER);
+ assertEquals(4, OsConstants.CAP_FSETID);
+ assertEquals(5, OsConstants.CAP_KILL);
+ assertEquals(6, OsConstants.CAP_SETGID);
+ assertEquals(7, OsConstants.CAP_SETUID);
+ assertEquals(8, OsConstants.CAP_SETPCAP);
+ assertEquals(9, OsConstants.CAP_LINUX_IMMUTABLE);
+ assertEquals(10, OsConstants.CAP_NET_BIND_SERVICE);
+ assertEquals(11, OsConstants.CAP_NET_BROADCAST);
+ assertEquals(12, OsConstants.CAP_NET_ADMIN);
+ assertEquals(13, OsConstants.CAP_NET_RAW);
+ assertEquals(14, OsConstants.CAP_IPC_LOCK);
+ assertEquals(15, OsConstants.CAP_IPC_OWNER);
+ assertEquals(16, OsConstants.CAP_SYS_MODULE);
+ assertEquals(17, OsConstants.CAP_SYS_RAWIO);
+ assertEquals(18, OsConstants.CAP_SYS_CHROOT);
+ assertEquals(19, OsConstants.CAP_SYS_PTRACE);
+ assertEquals(20, OsConstants.CAP_SYS_PACCT);
+ assertEquals(21, OsConstants.CAP_SYS_ADMIN);
+ assertEquals(22, OsConstants.CAP_SYS_BOOT);
+ assertEquals(23, OsConstants.CAP_SYS_NICE);
+ assertEquals(24, OsConstants.CAP_SYS_RESOURCE);
+ assertEquals(25, OsConstants.CAP_SYS_TIME);
+ assertEquals(26, OsConstants.CAP_SYS_TTY_CONFIG);
+ assertEquals(27, OsConstants.CAP_MKNOD);
+ assertEquals(28, OsConstants.CAP_LEASE);
+ assertEquals(29, OsConstants.CAP_AUDIT_WRITE);
+ assertEquals(30, OsConstants.CAP_AUDIT_CONTROL);
+ assertEquals(31, OsConstants.CAP_SETFCAP);
+ assertEquals(32, OsConstants.CAP_MAC_OVERRIDE);
+ assertEquals(33, OsConstants.CAP_MAC_ADMIN);
+ assertEquals(34, OsConstants.CAP_SYSLOG);
+ assertEquals(35, OsConstants.CAP_WAKE_ALARM);
+ assertEquals(36, OsConstants.CAP_BLOCK_SUSPEND);
+ // last constant
+ assertEquals(40, OsConstants.CAP_LAST_CAP);
+ }
+
+ @Test
+ public void test_CAP_TO_INDEX() {
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_CHOWN));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_OVERRIDE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_READ_SEARCH));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FOWNER));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FSETID));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_KILL));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETGID));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETUID));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETPCAP));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LINUX_IMMUTABLE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BIND_SERVICE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BROADCAST));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_ADMIN));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_RAW));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_LOCK));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_OWNER));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_MODULE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RAWIO));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_CHROOT));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PTRACE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PACCT));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_ADMIN));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_BOOT));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_NICE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RESOURCE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TIME));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TTY_CONFIG));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_MKNOD));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LEASE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_WRITE));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_CONTROL));
+ assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETFCAP));
+ assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_OVERRIDE));
+ assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_ADMIN));
+ assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_SYSLOG));
+ assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_WAKE_ALARM));
+ assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_BLOCK_SUSPEND));
+ }
+
+ @Test
+ public void test_CAP_TO_MASK() {
+ assertEquals(1 << 0, CAP_TO_MASK(OsConstants.CAP_CHOWN));
+ assertEquals(1 << 1, CAP_TO_MASK(OsConstants.CAP_DAC_OVERRIDE));
+ assertEquals(1 << 2, CAP_TO_MASK(OsConstants.CAP_DAC_READ_SEARCH));
+ assertEquals(1 << 3, CAP_TO_MASK(OsConstants.CAP_FOWNER));
+ assertEquals(1 << 4, CAP_TO_MASK(OsConstants.CAP_FSETID));
+ assertEquals(1 << 5, CAP_TO_MASK(OsConstants.CAP_KILL));
+ assertEquals(1 << 6, CAP_TO_MASK(OsConstants.CAP_SETGID));
+ assertEquals(1 << 7, CAP_TO_MASK(OsConstants.CAP_SETUID));
+ assertEquals(1 << 8, CAP_TO_MASK(OsConstants.CAP_SETPCAP));
+ assertEquals(1 << 9, CAP_TO_MASK(OsConstants.CAP_LINUX_IMMUTABLE));
+ assertEquals(1 << 10, CAP_TO_MASK(OsConstants.CAP_NET_BIND_SERVICE));
+ assertEquals(1 << 11, CAP_TO_MASK(OsConstants.CAP_NET_BROADCAST));
+ assertEquals(1 << 12, CAP_TO_MASK(OsConstants.CAP_NET_ADMIN));
+ assertEquals(1 << 13, CAP_TO_MASK(OsConstants.CAP_NET_RAW));
+ assertEquals(1 << 14, CAP_TO_MASK(OsConstants.CAP_IPC_LOCK));
+ assertEquals(1 << 15, CAP_TO_MASK(OsConstants.CAP_IPC_OWNER));
+ assertEquals(1 << 16, CAP_TO_MASK(OsConstants.CAP_SYS_MODULE));
+ assertEquals(1 << 17, CAP_TO_MASK(OsConstants.CAP_SYS_RAWIO));
+ assertEquals(1 << 18, CAP_TO_MASK(OsConstants.CAP_SYS_CHROOT));
+ assertEquals(1 << 19, CAP_TO_MASK(OsConstants.CAP_SYS_PTRACE));
+ assertEquals(1 << 20, CAP_TO_MASK(OsConstants.CAP_SYS_PACCT));
+ assertEquals(1 << 21, CAP_TO_MASK(OsConstants.CAP_SYS_ADMIN));
+ assertEquals(1 << 22, CAP_TO_MASK(OsConstants.CAP_SYS_BOOT));
+ assertEquals(1 << 23, CAP_TO_MASK(OsConstants.CAP_SYS_NICE));
+ assertEquals(1 << 24, CAP_TO_MASK(OsConstants.CAP_SYS_RESOURCE));
+ assertEquals(1 << 25, CAP_TO_MASK(OsConstants.CAP_SYS_TIME));
+ assertEquals(1 << 26, CAP_TO_MASK(OsConstants.CAP_SYS_TTY_CONFIG));
+ assertEquals(1 << 27, CAP_TO_MASK(OsConstants.CAP_MKNOD));
+ assertEquals(1 << 28, CAP_TO_MASK(OsConstants.CAP_LEASE));
+ assertEquals(1 << 29, CAP_TO_MASK(OsConstants.CAP_AUDIT_WRITE));
+ assertEquals(1 << 30, CAP_TO_MASK(OsConstants.CAP_AUDIT_CONTROL));
+ assertEquals(1 << 31, CAP_TO_MASK(OsConstants.CAP_SETFCAP));
+ assertEquals(1 << 0, CAP_TO_MASK(OsConstants.CAP_MAC_OVERRIDE));
+ assertEquals(1 << 1, CAP_TO_MASK(OsConstants.CAP_MAC_ADMIN));
+ assertEquals(1 << 2, CAP_TO_MASK(OsConstants.CAP_SYSLOG));
+ assertEquals(1 << 3, CAP_TO_MASK(OsConstants.CAP_WAKE_ALARM));
+ assertEquals(1 << 4, CAP_TO_MASK(OsConstants.CAP_BLOCK_SUSPEND));
+ }
+
+ @Test
+ public void test_S_ISLNK() {
+ assertTrue(S_ISLNK(OsConstants.S_IFLNK));
+
+ assertFalse(S_ISLNK(OsConstants.S_IFBLK));
+ assertFalse(S_ISLNK(OsConstants.S_IFCHR));
+ assertFalse(S_ISLNK(OsConstants.S_IFDIR));
+ assertFalse(S_ISLNK(OsConstants.S_IFIFO));
+ assertFalse(S_ISLNK(OsConstants.S_IFMT));
+ assertFalse(S_ISLNK(OsConstants.S_IFREG));
+ assertFalse(S_ISLNK(OsConstants.S_IFSOCK));
+ assertFalse(S_ISLNK(OsConstants.S_IRGRP));
+ assertFalse(S_ISLNK(OsConstants.S_IROTH));
+ assertFalse(S_ISLNK(OsConstants.S_IRUSR));
+ assertFalse(S_ISLNK(OsConstants.S_IRWXG));
+ assertFalse(S_ISLNK(OsConstants.S_IRWXO));
+ assertFalse(S_ISLNK(OsConstants.S_IRWXU));
+ assertFalse(S_ISLNK(OsConstants.S_ISGID));
+ assertFalse(S_ISLNK(OsConstants.S_ISUID));
+ assertFalse(S_ISLNK(OsConstants.S_ISVTX));
+ assertFalse(S_ISLNK(OsConstants.S_IWGRP));
+ assertFalse(S_ISLNK(OsConstants.S_IWOTH));
+ assertFalse(S_ISLNK(OsConstants.S_IWUSR));
+ assertFalse(S_ISLNK(OsConstants.S_IXGRP));
+ assertFalse(S_ISLNK(OsConstants.S_IXOTH));
+ assertFalse(S_ISLNK(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISREG() {
+ assertTrue(S_ISREG(OsConstants.S_IFREG));
+
+ assertFalse(S_ISREG(OsConstants.S_IFBLK));
+ assertFalse(S_ISREG(OsConstants.S_IFCHR));
+ assertFalse(S_ISREG(OsConstants.S_IFDIR));
+ assertFalse(S_ISREG(OsConstants.S_IFIFO));
+ assertFalse(S_ISREG(OsConstants.S_IFLNK));
+ assertFalse(S_ISREG(OsConstants.S_IFMT));
+ assertFalse(S_ISREG(OsConstants.S_IFSOCK));
+ assertFalse(S_ISREG(OsConstants.S_IRGRP));
+ assertFalse(S_ISREG(OsConstants.S_IROTH));
+ assertFalse(S_ISREG(OsConstants.S_IRUSR));
+ assertFalse(S_ISREG(OsConstants.S_IRWXG));
+ assertFalse(S_ISREG(OsConstants.S_IRWXO));
+ assertFalse(S_ISREG(OsConstants.S_IRWXU));
+ assertFalse(S_ISREG(OsConstants.S_ISGID));
+ assertFalse(S_ISREG(OsConstants.S_ISUID));
+ assertFalse(S_ISREG(OsConstants.S_ISVTX));
+ assertFalse(S_ISREG(OsConstants.S_IWGRP));
+ assertFalse(S_ISREG(OsConstants.S_IWOTH));
+ assertFalse(S_ISREG(OsConstants.S_IWUSR));
+ assertFalse(S_ISREG(OsConstants.S_IXGRP));
+ assertFalse(S_ISREG(OsConstants.S_IXOTH));
+ assertFalse(S_ISREG(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISDIR() {
+ assertTrue(S_ISDIR(OsConstants.S_IFDIR));
+
+ assertFalse(S_ISDIR(OsConstants.S_IFBLK));
+ assertFalse(S_ISDIR(OsConstants.S_IFCHR));
+ assertFalse(S_ISDIR(OsConstants.S_IFIFO));
+ assertFalse(S_ISDIR(OsConstants.S_IFLNK));
+ assertFalse(S_ISDIR(OsConstants.S_IFMT));
+ assertFalse(S_ISDIR(OsConstants.S_IFREG));
+ assertFalse(S_ISDIR(OsConstants.S_IFSOCK));
+ assertFalse(S_ISDIR(OsConstants.S_IRGRP));
+ assertFalse(S_ISDIR(OsConstants.S_IROTH));
+ assertFalse(S_ISDIR(OsConstants.S_IRUSR));
+ assertFalse(S_ISDIR(OsConstants.S_IRWXG));
+ assertFalse(S_ISDIR(OsConstants.S_IRWXO));
+ assertFalse(S_ISDIR(OsConstants.S_IRWXU));
+ assertFalse(S_ISDIR(OsConstants.S_ISGID));
+ assertFalse(S_ISDIR(OsConstants.S_ISUID));
+ assertFalse(S_ISDIR(OsConstants.S_ISVTX));
+ assertFalse(S_ISDIR(OsConstants.S_IWGRP));
+ assertFalse(S_ISDIR(OsConstants.S_IWOTH));
+ assertFalse(S_ISDIR(OsConstants.S_IWUSR));
+ assertFalse(S_ISDIR(OsConstants.S_IXGRP));
+ assertFalse(S_ISDIR(OsConstants.S_IXOTH));
+ assertFalse(S_ISDIR(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISCHR() {
+ assertTrue(S_ISCHR(OsConstants.S_IFCHR));
+
+ assertFalse(S_ISCHR(OsConstants.S_IFBLK));
+ assertFalse(S_ISCHR(OsConstants.S_IFDIR));
+ assertFalse(S_ISCHR(OsConstants.S_IFIFO));
+ assertFalse(S_ISCHR(OsConstants.S_IFLNK));
+ assertFalse(S_ISCHR(OsConstants.S_IFMT));
+ assertFalse(S_ISCHR(OsConstants.S_IFREG));
+ assertFalse(S_ISCHR(OsConstants.S_IFSOCK));
+ assertFalse(S_ISCHR(OsConstants.S_IRGRP));
+ assertFalse(S_ISCHR(OsConstants.S_IROTH));
+ assertFalse(S_ISCHR(OsConstants.S_IRUSR));
+ assertFalse(S_ISCHR(OsConstants.S_IRWXG));
+ assertFalse(S_ISCHR(OsConstants.S_IRWXO));
+ assertFalse(S_ISCHR(OsConstants.S_IRWXU));
+ assertFalse(S_ISCHR(OsConstants.S_ISGID));
+ assertFalse(S_ISCHR(OsConstants.S_ISUID));
+ assertFalse(S_ISCHR(OsConstants.S_ISVTX));
+ assertFalse(S_ISCHR(OsConstants.S_IWGRP));
+ assertFalse(S_ISCHR(OsConstants.S_IWOTH));
+ assertFalse(S_ISCHR(OsConstants.S_IWUSR));
+ assertFalse(S_ISCHR(OsConstants.S_IXGRP));
+ assertFalse(S_ISCHR(OsConstants.S_IXOTH));
+ assertFalse(S_ISCHR(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISBLK() {
+ assertTrue (S_ISBLK(OsConstants.S_IFBLK));
+
+ assertFalse(S_ISBLK(OsConstants.S_IFCHR));
+ assertFalse(S_ISBLK(OsConstants.S_IFDIR));
+ assertFalse(S_ISBLK(OsConstants.S_IFIFO));
+ assertFalse(S_ISBLK(OsConstants.S_IFLNK));
+ assertFalse(S_ISBLK(OsConstants.S_IFMT));
+ assertFalse(S_ISBLK(OsConstants.S_IFREG));
+ assertFalse(S_ISBLK(OsConstants.S_IFSOCK));
+ assertFalse(S_ISBLK(OsConstants.S_IRGRP));
+ assertFalse(S_ISBLK(OsConstants.S_IROTH));
+ assertFalse(S_ISBLK(OsConstants.S_IRUSR));
+ assertFalse(S_ISBLK(OsConstants.S_IRWXG));
+ assertFalse(S_ISBLK(OsConstants.S_IRWXO));
+ assertFalse(S_ISBLK(OsConstants.S_IRWXU));
+ assertFalse(S_ISBLK(OsConstants.S_ISGID));
+ assertFalse(S_ISBLK(OsConstants.S_ISUID));
+ assertFalse(S_ISBLK(OsConstants.S_ISVTX));
+ assertFalse(S_ISBLK(OsConstants.S_IWGRP));
+ assertFalse(S_ISBLK(OsConstants.S_IWOTH));
+ assertFalse(S_ISBLK(OsConstants.S_IWUSR));
+ assertFalse(S_ISBLK(OsConstants.S_IXGRP));
+ assertFalse(S_ISBLK(OsConstants.S_IXOTH));
+ assertFalse(S_ISBLK(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISFIFO() {
+ assertTrue(S_ISFIFO(OsConstants.S_IFIFO));
+
+ assertFalse(S_ISFIFO(OsConstants.S_IFBLK));
+ assertFalse(S_ISFIFO(OsConstants.S_IFCHR));
+ assertFalse(S_ISFIFO(OsConstants.S_IFDIR));
+ assertFalse(S_ISFIFO(OsConstants.S_IFLNK));
+ assertFalse(S_ISFIFO(OsConstants.S_IFMT));
+ assertFalse(S_ISFIFO(OsConstants.S_IFREG));
+ assertFalse(S_ISFIFO(OsConstants.S_IFSOCK));
+ assertFalse(S_ISFIFO(OsConstants.S_IRGRP));
+ assertFalse(S_ISFIFO(OsConstants.S_IROTH));
+ assertFalse(S_ISFIFO(OsConstants.S_IRUSR));
+ assertFalse(S_ISFIFO(OsConstants.S_IRWXG));
+ assertFalse(S_ISFIFO(OsConstants.S_IRWXO));
+ assertFalse(S_ISFIFO(OsConstants.S_IRWXU));
+ assertFalse(S_ISFIFO(OsConstants.S_ISGID));
+ assertFalse(S_ISFIFO(OsConstants.S_ISUID));
+ assertFalse(S_ISFIFO(OsConstants.S_ISVTX));
+ assertFalse(S_ISFIFO(OsConstants.S_IWGRP));
+ assertFalse(S_ISFIFO(OsConstants.S_IWOTH));
+ assertFalse(S_ISFIFO(OsConstants.S_IWUSR));
+ assertFalse(S_ISFIFO(OsConstants.S_IXGRP));
+ assertFalse(S_ISFIFO(OsConstants.S_IXOTH));
+ assertFalse(S_ISFIFO(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_S_ISSOCK() {
+ assertTrue(S_ISSOCK(OsConstants.S_IFSOCK));
+
+ assertFalse(S_ISSOCK(OsConstants.S_IFBLK));
+ assertFalse(S_ISSOCK(OsConstants.S_IFCHR));
+ assertFalse(S_ISSOCK(OsConstants.S_IFDIR));
+ assertFalse(S_ISSOCK(OsConstants.S_IFIFO));
+ assertFalse(S_ISSOCK(OsConstants.S_IFLNK));
+ assertFalse(S_ISSOCK(OsConstants.S_IFMT));
+ assertFalse(S_ISSOCK(OsConstants.S_IFREG));
+ assertFalse(S_ISSOCK(OsConstants.S_IRGRP));
+ assertFalse(S_ISSOCK(OsConstants.S_IROTH));
+ assertFalse(S_ISSOCK(OsConstants.S_IRUSR));
+ assertFalse(S_ISSOCK(OsConstants.S_IRWXG));
+ assertFalse(S_ISSOCK(OsConstants.S_IRWXO));
+ assertFalse(S_ISSOCK(OsConstants.S_IRWXU));
+ assertFalse(S_ISSOCK(OsConstants.S_ISGID));
+ assertFalse(S_ISSOCK(OsConstants.S_ISUID));
+ assertFalse(S_ISSOCK(OsConstants.S_ISVTX));
+ assertFalse(S_ISSOCK(OsConstants.S_IWGRP));
+ assertFalse(S_ISSOCK(OsConstants.S_IWOTH));
+ assertFalse(S_ISSOCK(OsConstants.S_IWUSR));
+ assertFalse(S_ISSOCK(OsConstants.S_IXGRP));
+ assertFalse(S_ISSOCK(OsConstants.S_IXOTH));
+ assertFalse(S_ISSOCK(OsConstants.S_IXUSR));
+ }
+
+ @Test
+ public void test_WEXITSTATUS() {
+ assertEquals(0, WEXITSTATUS(0x0000));
+ assertEquals(0, WEXITSTATUS(0x00DE));
+ assertEquals(0xF0, WEXITSTATUS(0xF000));
+ assertEquals(0xAB, WEXITSTATUS(0xAB12));
+ }
+
+ @Test
+ public void test_WCOREDUMP() {
+ assertFalse(WCOREDUMP(0));
+ assertTrue(WCOREDUMP(0x80));
+ }
+
+ @Test
+ public void test_WTERMSIG() {
+ assertEquals(0, WTERMSIG(0));
+ assertEquals(0x7f, WTERMSIG(0x7f));
+ }
+
+ @Test
+ public void test_WSTOPSIG() {
+ assertEquals(0, WSTOPSIG(0x0000));
+ assertEquals(0, WSTOPSIG(0x00DE));
+ assertEquals(0xF0, WSTOPSIG(0xF000));
+ assertEquals(0xAB, WSTOPSIG(0xAB12));
+ }
+
+
+ @Test
+ public void test_WIFEXITED() {
+ assertTrue(WIFEXITED(0));
+ assertFalse(WIFEXITED(0x7f));
+ }
+
+ @Test
+ public void test_WIFSTOPPED() {
+ assertFalse(WIFSTOPPED(0));
+ assertTrue(WIFSTOPPED(0x7f));
+ }
+
+ @Test
+ public void test_WIFSIGNALED() {
+ assertFalse(WIFSIGNALED(0));
+ assertTrue(WIFSIGNALED(1));
+ }
+}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
new file mode 100644
index 0000000..b5038e6
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ravenwood.runtimetest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.system.Os;
+import android.system.OsConstants;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.ravenwood.common.JvmWorkaround;
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+@RunWith(AndroidJUnit4.class)
+public class OsTest {
+ public interface ConsumerWithThrow<T> {
+ void accept(T var1) throws Exception;
+ }
+
+ private void withTestFile(ConsumerWithThrow<FileDescriptor> consumer) throws Exception {
+ File file = File.createTempFile("osTest", "bin");
+ try (var raf = new RandomAccessFile(file, "rw")) {
+ var fd = raf.getFD();
+
+ try (var os = new FileOutputStream(fd)) {
+ os.write(1);
+ os.write(2);
+ os.write(3);
+ os.write(4);
+
+ consumer.accept(fd);
+ }
+ }
+ }
+
+ @Test
+ public void testLseek() throws Exception {
+ withTestFile((fd) -> {
+ assertEquals(4, Os.lseek(fd, 4, OsConstants.SEEK_SET));
+ assertEquals(4, Os.lseek(fd, 0, OsConstants.SEEK_CUR));
+ assertEquals(6, Os.lseek(fd, 2, OsConstants.SEEK_CUR));
+ });
+ }
+
+ @Test
+ public void testDup() throws Exception {
+ withTestFile((fd) -> {
+ var dup = Os.dup(fd);
+
+ checkAreDup(fd, dup);
+ });
+ }
+
+ @Test
+ public void testPipe2() throws Exception {
+ var fds = Os.pipe2(0);
+
+ write(fds[1], 123);
+ assertEquals(123, read(fds[0]));
+ }
+
+ @Test
+ public void testFcntlInt() throws Exception {
+ withTestFile((fd) -> {
+ var dupInt = Os.fcntlInt(fd, 0, 0);
+
+ var dup = new FileDescriptor();
+ JvmWorkaround.getInstance().setFdInt(dup, dupInt);
+
+ checkAreDup(fd, dup);
+ });
+ }
+
+ private static void write(FileDescriptor fd, int oneByte) throws IOException {
+ // Create a dup to avoid closing the FD.
+ try (var dup = new FileOutputStream(RavenwoodRuntimeNative.dup(fd))) {
+ dup.write(oneByte);
+ }
+ }
+
+ private static int read(FileDescriptor fd) throws IOException {
+ // Create a dup to avoid closing the FD.
+ try (var dup = new FileInputStream(RavenwoodRuntimeNative.dup(fd))) {
+ return dup.read();
+ }
+ }
+
+ private static void checkAreDup(FileDescriptor fd1, FileDescriptor fd2) throws Exception {
+ assertEquals(4, Os.lseek(fd1, 4, OsConstants.SEEK_SET));
+ assertEquals(4, Os.lseek(fd1, 0, OsConstants.SEEK_CUR));
+
+ // Dup'ed FD shares the same position.
+ assertEquals(4, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+
+ assertEquals(6, Os.lseek(fd1, 2, OsConstants.SEEK_CUR));
+ assertEquals(6, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
index b7f12ad..c41a8cd 100644
--- a/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SaveEventLogger.java
@@ -23,6 +23,7 @@
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NONE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NO_SAVE_INFO;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_NO_VALUE_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SESSION_DESTROYED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG;
@@ -112,6 +113,8 @@
AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SESSION_DESTROYED;
public static final int NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG =
AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG;
+ public static final int NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD =
+ AUTOFILL_SAVE_EVENT_REPORTED__SAVE_UI_NOT_SHOWN_REASON__NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD;
public static final long UNINITIATED_TIMESTAMP = Long.MIN_VALUE;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a8b1235..c46464b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -89,6 +89,7 @@
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NONE;
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NO_SAVE_INFO;
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_NO_VALUE_CHANGED;
+import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD;
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_SESSION_DESTROYED;
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG;
import static com.android.server.autofill.SaveEventLogger.NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG;
@@ -3673,6 +3674,8 @@
Slog.v(TAG, "Call to Session#showSaveLocked() rejected - "
+ "there is credman field in screen");
}
+ mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD);
+ mSaveEventLogger.logAndEndEvent();
return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_NONE);
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b414b25..2d99c96 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -270,7 +270,8 @@
PackageManagerInternal.class);
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
mBackupManagerService.getPackageManager(), allowApks, info, signatures,
- pmi, mUserId, mBackupEligibilityRules);
+ pmi, mUserId, mBackupEligibilityRules,
+ mBackupManagerService.getContext());
mManifestSignatures.put(info.packageName, signatures);
mPackagePolicies.put(pkg, restorePolicy);
mPackageInstallers.put(pkg, info.installerPackageName);
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 78a9952..4860a27 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -31,6 +31,7 @@
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
@@ -53,17 +54,22 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
import com.android.server.backup.restore.RestorePolicy;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
/**
* Utility methods to read backup tar file.
@@ -390,7 +396,7 @@
boolean allowApks, FileMetadata info, Signature[] signatures,
PackageManagerInternal pmi, int userId, Context context) {
return chooseRestorePolicy(packageManager, allowApks, info, signatures, pmi, userId,
- BackupEligibilityRules.forBackup(packageManager, pmi, userId, context));
+ BackupEligibilityRules.forBackup(packageManager, pmi, userId, context), context);
}
/**
@@ -406,7 +412,8 @@
*/
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
boolean allowApks, FileMetadata info, Signature[] signatures,
- PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules) {
+ PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules,
+ Context context) {
if (signatures == null) {
return RestorePolicy.IGNORE;
}
@@ -448,6 +455,16 @@
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
null);
+ } else if (isAllowlistedForVToURestore(info, pkgInfo, userId, context)) {
+ Slog.i(TAG, "Performing a V to U downgrade; package: "
+ + info.packageName
+ + " is allowlisted");
+ policy = RestorePolicy.ACCEPT;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE,
+ pkgInfo,
+ LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ null);
} else {
// The data is from a newer version of the app than
// is presently installed. That means we can only
@@ -751,6 +768,36 @@
return true;
}
+ // checks the sdk of the target/source device for a B&R operation.
+ // system components can opt in of V->U restore via allowlist.
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ private boolean isAllowlistedForVToURestore(FileMetadata backupFileInfo,
+ PackageInfo installedPackageInfo,
+ int userId, Context context) {
+ // We assume that the package version matches the sdk (e.g. version 35 means V).
+ // This is true for most of the system components ( and it is specifically true for those
+ // that are in the allowlist)
+ // In order to check if this is a V to U transfer we check if the package version from the
+ // backup is 35 and on the target is 34.
+ // We don't need to check the V to U denylist here since a package can only make it
+ // to TarBackupReader if allowed and not denied (from PerformUnifiedRestoreTask)
+
+ String vToUAllowlist = getVToUAllowlist(context, userId);
+ List<String> mVToUAllowlist = Arrays.asList(vToUAllowlist.split(","));
+ return Flags.enableVToURestoreForSystemComponentsInAllowlist()
+ && (installedPackageInfo.getLongVersionCode()
+ == Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ && (backupFileInfo.version > Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ && (mVToUAllowlist.contains(installedPackageInfo.packageName));
+ }
+
+ private String getVToUAllowlist(Context context, int userId) {
+ return Settings.Secure.getStringForUser(
+ context.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ userId);
+ }
+
private static long extractRadix(byte[] data, int offset, int maxChars, int radix)
throws IOException {
long value = 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a5d4a3..f7278e9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6025,46 +6025,47 @@
}
synchronized (mProcLock) {
- synchronized (mPidsSelfLocked) {
- int newestTimeIndex = -1;
- long newestTime = Long.MIN_VALUE;
- for (int i = 0; i < pids.length; i++) {
- ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
- if (pr != null) {
- final long pendingTopTime =
- mPendingStartActivityUids.getPendingTopPidTime(pr.uid, pids[i]);
- if (pendingTopTime != PendingStartActivityUids.INVALID_TIME) {
- // The uid in mPendingStartActivityUids gets the TOP process state.
- states[i] = PROCESS_STATE_TOP;
- if (scores != null) {
- // The uid in mPendingStartActivityUids gets a better score.
- scores[i] = ProcessList.FOREGROUND_APP_ADJ - 1;
- }
- if (pendingTopTime > newestTime) {
- newestTimeIndex = i;
- newestTime = pendingTopTime;
- }
- } else {
- states[i] = pr.mState.getCurProcState();
- if (scores != null) {
- scores[i] = pr.mState.getCurAdj();
- }
+ int newestTimeIndex = -1;
+ long newestTime = Long.MIN_VALUE;
+ for (int i = 0; i < pids.length; i++) {
+ final ProcessRecord pr;
+ synchronized (mPidsSelfLocked) {
+ pr = mPidsSelfLocked.get(pids[i]);
+ }
+ if (pr != null) {
+ final long pendingTopTime =
+ mPendingStartActivityUids.getPendingTopPidTime(pr.uid, pids[i]);
+ if (pendingTopTime != PendingStartActivityUids.INVALID_TIME) {
+ // The uid in mPendingStartActivityUids gets the TOP process state.
+ states[i] = PROCESS_STATE_TOP;
+ if (scores != null) {
+ // The uid in mPendingStartActivityUids gets a better score.
+ scores[i] = ProcessList.FOREGROUND_APP_ADJ - 1;
+ }
+ if (pendingTopTime > newestTime) {
+ newestTimeIndex = i;
+ newestTime = pendingTopTime;
}
} else {
- states[i] = PROCESS_STATE_NONEXISTENT;
+ states[i] = pr.mState.getCurProcState();
if (scores != null) {
- scores[i] = ProcessList.INVALID_ADJ;
+ scores[i] = pr.mState.getCurAdj();
}
}
- }
- // The uid with the newest timestamp in mPendingStartActivityUids gets the best
- // score.
- if (newestTimeIndex != -1) {
+ } else {
+ states[i] = PROCESS_STATE_NONEXISTENT;
if (scores != null) {
- scores[newestTimeIndex] = ProcessList.FOREGROUND_APP_ADJ - 2;
+ scores[i] = ProcessList.INVALID_ADJ;
}
}
}
+ // The uid with the newest timestamp in mPendingStartActivityUids gets the best
+ // score.
+ if (newestTimeIndex != -1) {
+ if (scores != null) {
+ scores[newestTimeIndex] = ProcessList.FOREGROUND_APP_ADJ - 2;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 79a8518..a8227fa 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -83,6 +83,7 @@
private static final int FOREACH_ACTION_NONE = 0;
private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
private static final int FOREACH_ACTION_STOP_ITERATION = 2;
+ private static final int FOREACH_ACTION_REMOVE_AND_STOP_ITERATION = 3;
private static final String MONITORING_MODE_EMPTY_TEXT = "No records";
@@ -659,8 +660,13 @@
}
}
+ /**
+ * Run provided callback for each packake in start info dataset.
+ *
+ * @return whether the for each completed naturally, false if it was stopped manually.
+ */
@GuardedBy("mLock")
- private void forEachPackageLocked(
+ private boolean forEachPackageLocked(
BiFunction<String, SparseArray<AppStartInfoContainer>, Integer> callback) {
if (callback != null) {
ArrayMap<String, SparseArray<AppStartInfoContainer>> map = mData.getMap();
@@ -670,14 +676,17 @@
map.removeAt(i);
break;
case FOREACH_ACTION_STOP_ITERATION:
- i = 0;
- break;
+ return false;
+ case FOREACH_ACTION_REMOVE_AND_STOP_ITERATION:
+ map.removeAt(i);
+ return false;
case FOREACH_ACTION_NONE:
default:
break;
}
}
}
+ return true;
}
@GuardedBy("mLock")
@@ -870,13 +879,14 @@
}
AtomicFile af = new AtomicFile(mProcStartInfoFile);
FileOutputStream out = null;
+ boolean succeeded;
long now = System.currentTimeMillis();
try {
out = af.startWrite();
ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(AppsStartInfoProto.LAST_UPDATE_TIMESTAMP, now);
synchronized (mLock) {
- forEachPackageLocked(
+ succeeded = forEachPackageLocked(
(packageName, records) -> {
long token = proto.start(AppsStartInfoProto.PACKAGES);
proto.write(AppsStartInfoProto.Package.PACKAGE_NAME, packageName);
@@ -884,19 +894,30 @@
for (int j = 0; j < uidArraySize; j++) {
try {
records.valueAt(j)
- .writeToProto(proto, AppsStartInfoProto.Package.USERS);
+ .writeToProto(proto, AppsStartInfoProto.Package.USERS);
} catch (IOException e) {
Slog.w(TAG, "Unable to write app start info into persistent"
+ "storage: " + e);
+ // There was likely an issue with this record that won't resolve
+ // next time we try to persist so remove it. Also stop iteration
+ // as we failed the write and need to start again from scratch.
+ return AppStartInfoTracker
+ .FOREACH_ACTION_REMOVE_AND_STOP_ITERATION;
}
}
proto.end(token);
return AppStartInfoTracker.FOREACH_ACTION_NONE;
});
- mLastAppStartInfoPersistTimestamp = now;
+ if (succeeded) {
+ mLastAppStartInfoPersistTimestamp = now;
+ }
}
- proto.flush();
- af.finishWrite(out);
+ if (succeeded) {
+ proto.flush();
+ af.finishWrite(out);
+ } else {
+ af.failWrite(out);
+ }
} catch (IOException e) {
Slog.w(TAG, "Unable to write historical app start info into persistent storage: " + e);
af.failWrite(out);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 211f952..db4840d 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -83,6 +83,8 @@
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;
@@ -98,8 +100,6 @@
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
- + " to frozen apps and got error " + err);
+ + " 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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fa8832c..219de70 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -36,6 +36,7 @@
import static android.os.Process.startWebView;
import static android.system.OsConstants.EAGAIN;
+import static com.android.sdksandbox.flags.Flags.selinuxInputSelector;
import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -2066,11 +2067,16 @@
}
}
- return app.info.seInfo
- + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser) + extraInfo;
+ // The order of selectors in seInfo matters, the string is terminated by the word complete.
+ if (selinuxInputSelector()) {
+ return app.info.seInfo + extraInfo + TextUtils.emptyIfNull(app.info.seInfoUser);
+ } else {
+ return app.info.seInfo
+ + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser)
+ + extraInfo;
+ }
}
-
@GuardedBy("mService")
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7deef2f..72c5254 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2123,7 +2123,10 @@
return AudioProductStrategy.getAudioProductStrategies();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(anyOf = {
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING
+ })
/**
* @return the List of {@link android.media.audiopolicy.AudioVolumeGroup} discovered from the
* platform configuration file.
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 30d12e6..1949e6f 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -56,10 +56,12 @@
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
/**
* Manages the associated display brightness when in auto-brightness mode. This is also
@@ -206,7 +208,7 @@
private float mScreenBrighteningThreshold;
private float mScreenDarkeningThreshold;
// The most recent light sample.
- private float mLastObservedLux = INVALID_LUX;
+ private float mLastObservedLux;
// The time of the most light recent sample.
private long mLastObservedLuxTime;
@@ -277,6 +279,8 @@
private Clock mClock;
private final Injector mInjector;
+ private final DisplayManagerFlags mDisplayManagerFlags;
+
AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, Sensor lightSensor,
SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap,
@@ -291,7 +295,8 @@
BrightnessRangeController brightnessModeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController) {
+ BrightnessClamperController brightnessClamperController,
+ DisplayManagerFlags displayManagerFlags) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
dozeScaleFactor, lightSensorRate, initialLightSensorRate,
@@ -301,7 +306,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController
+ userNits, brightnessClamperController, displayManagerFlags
);
}
@@ -320,9 +325,10 @@
BrightnessRangeController brightnessRangeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController) {
+ BrightnessClamperController brightnessClamperController,
+ DisplayManagerFlags displayManagerFlags) {
mInjector = injector;
- mClock = injector.createClock();
+ mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
mContext = context;
mCallbacks = callbacks;
mSensorManager = sensorManager;
@@ -367,6 +373,7 @@
mBrightnessClamperController = brightnessClamperController;
mBrightnessThrottler = brightnessThrottler;
mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
+ mDisplayManagerFlags = displayManagerFlags;
// Use the given short-term model
if (userNits != BrightnessMappingStrategy.INVALID_NITS) {
@@ -429,34 +436,6 @@
return mRawScreenAutoBrightness;
}
- /**
- * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when
- * entering doze - we disable the light sensor, invalidate the lux, but we still need to set
- * the initial brightness in doze mode.
- */
- public float getAutomaticScreenBrightnessBasedOnLastUsedLux(
- BrightnessEvent brightnessEvent) {
- float lastUsedLux = mAmbientLux;
- if (lastUsedLux == INVALID_LUX) {
- return PowerManager.BRIGHTNESS_INVALID_FLOAT;
- }
-
- float brightness = mCurrentBrightnessMapper.getBrightness(lastUsedLux,
- mForegroundAppPackageName, mForegroundAppCategory);
- if (shouldApplyDozeScaleFactor()) {
- brightness *= mDozeScaleFactor;
- }
-
- if (brightnessEvent != null) {
- brightnessEvent.setLux(lastUsedLux);
- brightnessEvent.setRecommendedBrightness(brightness);
- brightnessEvent.setFlags(brightnessEvent.getFlags()
- | (shouldApplyDozeScaleFactor() ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
- brightnessEvent.setAutoBrightnessMode(getMode());
- }
- return brightness;
- }
-
public boolean hasValidAmbientLux() {
return mAmbientLuxValid;
}
@@ -747,7 +726,6 @@
mRecentLightSamples++;
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
mAmbientLightRingBuffer.push(time, lux);
-
// Remember this sample value.
mLastObservedLux = lux;
mLastObservedLuxTime = time;
@@ -891,7 +869,7 @@
}
private void updateAmbientLux() {
- long time = mClock.uptimeMillis();
+ long time = mClock.getSensorEventScaleTime();
mAmbientLightRingBuffer.prune(time - mAmbientLightHorizonLong);
updateAmbientLux(time);
}
@@ -968,7 +946,16 @@
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
- mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
+
+ // The nextTransitionTime is computed as elapsedTime(Which also accounts for the time when
+ // android was sleeping) as the main reference. However, handlers work on the uptime(Not
+ // accounting for the time when android was sleeping)
+ mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
+ convertToUptime(nextTransitionTime));
+ }
+
+ private long convertToUptime(long time) {
+ return time - mClock.getSensorEventScaleTime() + mClock.uptimeMillis();
}
private void updateAutoBrightness(boolean sendUpdate, boolean isManuallySet) {
@@ -1185,15 +1172,13 @@
}
mPausedShortTermModel.copyFrom(tempShortTermModel);
}
-
- update();
}
/**
* Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes
* care of resetting the short term model wherever required
*/
- public void switchMode(@AutomaticBrightnessMode int mode) {
+ public void switchMode(@AutomaticBrightnessMode int mode, boolean sendUpdate) {
if (!mBrightnessMappingStrategyMap.contains(mode)) {
return;
}
@@ -1208,6 +1193,11 @@
resetShortTermModel();
mCurrentBrightnessMapper = mBrightnessMappingStrategyMap.get(mode);
}
+ if (sendUpdate) {
+ update();
+ } else {
+ updateAutoBrightness(/* sendUpdate= */ false, /* isManuallySet= */ false);
+ }
}
float getUserLux() {
@@ -1391,7 +1381,9 @@
@Override
public void onSensorChanged(SensorEvent event) {
if (mLightSensorEnabled) {
- final long time = mClock.uptimeMillis();
+ // The time received from the sensor is in nano seconds, hence changing it to ms
+ final long time = (mDisplayManagerFlags.offloadControlsDozeAutoBrightness())
+ ? TimeUnit.NANOSECONDS.toMillis(event.timestamp) : mClock.uptimeMillis();
final float lux = event.values[0];
handleLightSensorEvent(time, lux);
}
@@ -1424,6 +1416,12 @@
* Returns current time in milliseconds since boot, not counting time spent in deep sleep.
*/
long uptimeMillis();
+
+ /**
+ * Gets the time on either the elapsedTime or the uptime scale, depending on how we
+ * processing the events from the sensor
+ */
+ long getSensorEventScaleTime();
}
/**
@@ -1571,7 +1569,8 @@
StringBuilder buf = new StringBuilder();
buf.append('[');
for (int i = 0; i < mCount; i++) {
- final long next = i + 1 < mCount ? getTime(i + 1) : mClock.uptimeMillis();
+ final long next = i + 1 < mCount ? getTime(i + 1)
+ : mClock.getSensorEventScaleTime();
if (i != 0) {
buf.append(", ");
}
@@ -1596,13 +1595,31 @@
}
}
+ private static class RealClock implements Clock {
+ private final boolean mOffloadControlsDozeBrightness;
+
+ RealClock(boolean offloadControlsDozeBrightness) {
+ mOffloadControlsDozeBrightness = offloadControlsDozeBrightness;
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ public long getSensorEventScaleTime() {
+ return (mOffloadControlsDozeBrightness)
+ ? SystemClock.elapsedRealtime() : uptimeMillis();
+ }
+ }
+
public static class Injector {
public Handler getBackgroundThreadHandler() {
return BackgroundThread.getHandler();
}
- Clock createClock() {
- return SystemClock::uptimeMillis;
+ Clock createClock(boolean offloadControlsDozeBrightness) {
+ return new RealClock(offloadControlsDozeBrightness);
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 65c9f35..f77a360 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -52,6 +52,14 @@
}
@Override
+ public boolean allowAutoBrightnessInDoze() {
+ if (mDisplayOffloader == null) {
+ return false;
+ }
+ return mDisplayOffloader.allowAutoBrightnessInDoze();
+ }
+
+ @Override
public void updateBrightness(float brightness) {
if (mIsActive) {
mDisplayPowerController.setBrightnessFromOffload(brightness);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 0fcdf19..7d482f7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1115,7 +1115,7 @@
screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits,
- mBrightnessClamperController);
+ mBrightnessClamperController, mFlags);
mDisplayBrightnessController.setUpAutoBrightness(
mAutomaticBrightnessController, mSensorManager, mDisplayDeviceConfig, mHandler,
defaultModeBrightnessMapper, mIsEnabled, mLeadDisplayId);
@@ -1185,7 +1185,8 @@
@AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE;
if (mAutomaticBrightnessController != null) {
- mAutomaticBrightnessController.switchMode(mode);
+ // Set sendUpdate to true to make sure that updatePowerState() gets called
+ mAutomaticBrightnessController.switchMode(mode, /* sendUpdate= */ true);
setAnimatorRampSpeeds(isIdle);
}
Message msg = mHandler.obtainMessage();
@@ -1334,7 +1335,6 @@
mDisplayStateController.shouldPerformScreenOffTransition());
state = mPowerState.getScreenState();
-
DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
.updateBrightness(mPowerRequest, state);
float brightnessState = displayBrightnessState.getBrightness();
@@ -1366,17 +1366,26 @@
// request changes.
final boolean wasShortTermModelActive =
mAutomaticBrightnessStrategy.isShortTermModelActive();
+ boolean allowAutoBrightnessWhileDozing =
+ mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
+ if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled()
+ && mDisplayOffloadSession != null) {
+ allowAutoBrightnessWhileDozing &= mDisplayOffloadSession.allowAutoBrightnessInDoze();
+ }
if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
// Switch to doze auto-brightness mode if needed
if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
&& !mAutomaticBrightnessController.isInIdleMode()) {
+ // Set sendUpdate to false, we're already in updatePowerState() so there's no need
+ // to trigger it again
mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
- ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+ ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* sendUpdate= */ false);
}
mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
- mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
- mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
+ allowAutoBrightnessWhileDozing, mBrightnessReasonTemp.getReason(),
+ mPowerRequest.policy,
mDisplayBrightnessController.getLastUserSetScreenBrightness(),
userSetBrightnessChanged);
}
@@ -1443,47 +1452,27 @@
}
if (Display.isDozeState(state)) {
- // If there's an offload session, we need to set the initial doze brightness before
- // the offload session starts controlling the brightness.
- // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
- // will be selected again, meaning that no new brightness will be sent to the hardware
- // and the display will stay at the brightness level set by the offload session.
+ // TODO(b/329676661): Introduce a config property to choose between this brightness
+ // strategy and DOZE_DEFAULT
+ // On some devices, when auto-brightness is disabled and the device is dozing, we use
+ // the current brightness setting scaled by the doze scale factor
if ((Float.isNaN(brightnessState)
|| displayBrightnessState.getDisplayBrightnessStrategyName()
.equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME))
&& mFlags.isDisplayOffloadEnabled()
- && mDisplayOffloadSession != null) {
- if (mAutomaticBrightnessController != null
- && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
- // Use the auto-brightness curve and the last observed lux
- rawBrightnessState = mAutomaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(
- mTempBrightnessEvent);
- } else {
- rawBrightnessState = getDozeBrightnessForOffload();
- mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
- | BrightnessEvent.FLAG_DOZE_SCALE);
- }
-
- if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
- brightnessState = clampScreenBrightness(rawBrightnessState);
- mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
-
- if (mAutomaticBrightnessController != null
- && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
- // Keep the brightness in the setting so that we can use it after the screen
- // turns on, until a lux sample becomes available. We don't do this when
- // auto-brightness is disabled - in that situation we still want to use
- // the last brightness from when the screen was on.
- updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
- }
- }
+ && mDisplayOffloadSession != null
+ && (mAutomaticBrightnessController == null
+ || !mAutomaticBrightnessStrategy.shouldUseAutoBrightness())) {
+ rawBrightnessState = getDozeBrightnessForOffload();
+ brightnessState = clampScreenBrightness(rawBrightnessState);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_MANUAL);
+ mTempBrightnessEvent.setFlags(
+ mTempBrightnessEvent.getFlags() | BrightnessEvent.FLAG_DOZE_SCALE);
}
// Use default brightness when dozing unless overridden.
- if (Float.isNaN(brightnessState)
- || displayBrightnessState.getDisplayBrightnessStrategyName()
- .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) {
+ if (Float.isNaN(brightnessState) && Display.isDozeState(state)
+ && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()) {
rawBrightnessState = mScreenBrightnessDozeConfig;
brightnessState = clampScreenBrightness(rawBrightnessState);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
@@ -3169,7 +3158,8 @@
BrightnessRangeController brightnessModeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController) {
+ BrightnessClamperController brightnessClamperController,
+ DisplayManagerFlags displayManagerFlags) {
return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor,
brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin,
@@ -3180,7 +3170,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController);
+ userNits, brightnessClamperController, displayManagerFlags);
}
BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index b24caf4..44c8d1c 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -136,6 +136,9 @@
handleExternalDisplayConnectedLocked(logicalDisplay);
}
}
+ if (!mDisplayIdsWaitingForBootCompletion.isEmpty()) {
+ mLogicalDisplayMapper.updateLogicalDisplaysLocked();
+ }
mDisplayIdsWaitingForBootCompletion.clear();
}
@@ -222,7 +225,7 @@
} else {
// As external display is enabled by default, need to disable it now.
// TODO(b/292196201) Remove when the display can be disabled before DPC is created.
- mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, false);
+ mLogicalDisplayMapper.setEnabledLocked(logicalDisplay, false);
}
if (!isExternalDisplayAllowed()) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 01485cb..e645e98 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -1195,7 +1195,6 @@
return display;
}
- @VisibleForTesting
void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
final int displayId = display.getDisplayIdLocked();
final DisplayInfo info = display.getDisplayInfoLocked();
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index fc95d15..9bf10a7 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -40,8 +40,8 @@
public static final int REASON_SCREEN_OFF_BRIGHTNESS_SENSOR = 9;
public static final int REASON_FOLLOWER = 10;
public static final int REASON_OFFLOAD = 11;
- public static final int REASON_DOZE_INITIAL = 12;
- public static final int REASON_MAX = REASON_DOZE_INITIAL;
+ public static final int REASON_DOZE_MANUAL = 12;
+ public static final int REASON_MAX = REASON_DOZE_MANUAL;
public static final int MODIFIER_DIMMED = 0x1;
public static final int MODIFIER_LOW_POWER = 0x2;
@@ -208,8 +208,8 @@
return "follower";
case REASON_OFFLOAD:
return "offload";
- case REASON_DOZE_INITIAL:
- return "doze_initial";
+ case REASON_DOZE_MANUAL:
+ return "doze_manual";
default:
return Integer.toString(reason);
}
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 37b6931..2907364 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
@@ -15,8 +15,6 @@
*/
package com.android.server.display.brightness.strategy;
-import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
-
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
@@ -129,9 +127,11 @@
public void setAutoBrightnessState(int targetDisplayState,
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
- switchMode(targetDisplayState);
+ // We are still in the process of updating the power state, so there's no need to trigger
+ // an update again
+ switchMode(targetDisplayState, /* sendUpdate= */ false);
final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
+ allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState);
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
&& (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
@@ -371,23 +371,6 @@
}
/**
- * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when
- * entering doze - we disable the light sensor, invalidate the lux, but we still need to set
- * the initial brightness in doze mode.
- * @param brightnessEvent Event object to populate with details about why the specific
- * brightness was chosen.
- */
- public float getAutomaticScreenBrightnessBasedOnLastUsedLux(
- BrightnessEvent brightnessEvent) {
- float brightness = (mAutomaticBrightnessController != null)
- ? mAutomaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(brightnessEvent)
- : PowerManager.BRIGHTNESS_INVALID_FLOAT;
- adjustAutomaticBrightnessStateIfValid(brightness);
- return brightness;
- }
-
- /**
* Returns if the auto brightness has been applied
*/
public boolean hasAppliedAutoBrightness() {
@@ -495,14 +478,12 @@
mIsShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
}
}
-
-
- private void switchMode(int state) {
+ private void switchMode(int state, boolean sendUpdate) {
if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
&& mAutomaticBrightnessController != null
&& !mAutomaticBrightnessController.isInIdleMode()) {
mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
- ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+ ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
index 58670c9..4d9c18a 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
@@ -15,8 +15,6 @@
*/
package com.android.server.display.brightness.strategy;
-import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
-
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
@@ -110,7 +108,7 @@
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
+ allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState);
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
&& (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
@@ -273,23 +271,6 @@
}
/**
- * Get the automatic screen brightness based on the last observed lux reading. Used e.g. when
- * entering doze - we disable the light sensor, invalidate the lux, but we still need to set
- * the initial brightness in doze mode.
- * @param brightnessEvent Event object to populate with details about why the specific
- * brightness was chosen.
- */
- public float getAutomaticScreenBrightnessBasedOnLastUsedLux(
- BrightnessEvent brightnessEvent) {
- float brightness = (mAutomaticBrightnessController != null)
- ? mAutomaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(brightnessEvent)
- : PowerManager.BRIGHTNESS_INVALID_FLOAT;
- adjustAutomaticBrightnessStateIfValid(brightness);
- return brightness;
- }
-
- /**
* Gets the auto-brightness adjustment flag change reason
*/
public int getAutoBrightnessAdjustmentReasonsFlags() {
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 a5414fc..8f775a5 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -154,6 +154,11 @@
Flags::useFusionProxSensor
);
+ private final FlagState mOffloadControlsDozeAutoBrightness = new FlagState(
+ Flags.FLAG_OFFLOAD_CONTROLS_DOZE_AUTO_BRIGHTNESS,
+ Flags::offloadControlsDozeAutoBrightness
+ );
+
private final FlagState mPeakRefreshRatePhysicalLimit = new FlagState(
Flags.FLAG_ENABLE_PEAK_REFRESH_RATE_PHYSICAL_LIMIT,
Flags::enablePeakRefreshRatePhysicalLimit
@@ -327,6 +332,13 @@
return mUseFusionProxSensor.getName();
}
+ /**
+ * @return Whether DisplayOffload should control auto-brightness in doze
+ */
+ public boolean offloadControlsDozeAutoBrightness() {
+ return mOffloadControlsDozeAutoBrightness.isEnabled();
+ }
+
public boolean isPeakRefreshRatePhysicalLimitEnabled() {
return mPeakRefreshRatePhysicalLimit.isEnabled();
}
@@ -373,6 +385,7 @@
pw.println(" " + mRefactorDisplayPowerController);
pw.println(" " + mResolutionBackupRestore);
pw.println(" " + mUseFusionProxSensor);
+ pw.println(" " + mOffloadControlsDozeAutoBrightness);
pw.println(" " + mPeakRefreshRatePhysicalLimit);
pw.println(" " + mIgnoreAppPreferredRefreshRate);
pw.println(" " + mSynthetic60hzModes);
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 316b6db..697218d 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -246,6 +246,17 @@
}
flag {
+ name: "offload_controls_doze_auto_brightness"
+ namespace: "display_manager"
+ description: "Allows the registered DisplayOffloader to control if auto-brightness is used in doze"
+ bug: "327392714"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_peak_refresh_rate_physical_limit"
namespace: "display_manager"
description: "Flag for adding physical refresh rate limit if smooth display setting is on "
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 e20ac73..76a2827 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1116,6 +1116,9 @@
if (mIsLowPower) {
for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i);
+ if (config == null) {
+ continue;
+ }
List<SupportedModeData> supportedModes = config
.getRefreshRateData().lowPowerSupportedModes;
mVotesStorage.updateVote(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index f992a23..88da6fb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -100,6 +100,9 @@
// Map from port ID to HdmiDeviceInfo.
private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
+ // Cached physical address.
+ private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+
HdmiCecNetwork(HdmiControlService hdmiControlService,
HdmiCecController hdmiCecController,
HdmiMhlControllerStub hdmiMhlController) {
@@ -431,6 +434,8 @@
// each port. Return empty array if CEC HAL didn't provide the info.
if (mHdmiCecController != null) {
cecPortInfo = mHdmiCecController.getPortInfos();
+ // Invalid cached physical address.
+ mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
}
if (cecPortInfo == null) {
return;
@@ -856,7 +861,10 @@
}
public int getPhysicalAddress() {
- return mHdmiCecController.getPhysicalAddress();
+ if (mPhysicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+ mPhysicalAddress = mHdmiCecController.getPhysicalAddress();
+ }
+ return mPhysicalAddress;
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index cca73b5..dbd1e65 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1567,7 +1567,7 @@
* Returns physical address of the device.
*/
int getPhysicalAddress() {
- return mCecController.getPhysicalAddress();
+ return mHdmiCecNetwork.getPhysicalAddress();
}
/**
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
index 4ee2e99..6da7a65 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
@@ -172,17 +172,22 @@
@Override
public String toString() {
- String out = ContextHubTransaction.typeToString(mTransactionType, true /* upperCase */)
- + " (";
+ StringBuilder out = new StringBuilder();
+ out.append(ContextHubTransaction.typeToString(mTransactionType,
+ /* upperCase= */ true));
+ out.append(" (");
if (mNanoAppId != null) {
- out += "appId = 0x" + Long.toHexString(mNanoAppId) + ", ";
+ out.append("appId = 0x");
+ out.append(Long.toHexString(mNanoAppId));
+ out.append(", ");
}
- out += "package = " + mPackage;
+ out.append("package = ");
+ out.append(mPackage);
if (mMessageSequenceNumber != null) {
- out += ", messageSequenceNumber = " + mMessageSequenceNumber;
+ out.append(", messageSequenceNumber = ");
+ out.append(mMessageSequenceNumber);
}
- out += ")";
-
- return out;
+ out.append(")");
+ return out.toString();
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 39df5be..286e789 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -1181,7 +1181,7 @@
GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
}
} else if ("request_power_stats".equals(command)) {
- mGnssNative.requestPowerStats();
+ mGnssNative.requestPowerStats(Runnable::run, powerStats -> {});
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 133704d..6a72cc7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -314,9 +314,9 @@
ipw.decreaseIndent();
}
- GnssPowerStats powerStats = mGnssNative.getPowerStats();
+ GnssPowerStats powerStats = mGnssNative.getLastKnownPowerStats();
if (powerStats != null) {
- ipw.println("Last Power Stats:");
+ ipw.println("Last Known Power Stats:");
ipw.increaseIndent();
powerStats.dump(fd, ipw, args, mGnssNative.getCapabilities());
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/location/gnss/GnssMetrics.java b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
index dbc903d..ae79b01 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMetrics.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
@@ -16,6 +16,7 @@
package com.android.server.location.gnss;
+import android.annotation.NonNull;
import android.app.StatsManager;
import android.content.Context;
import android.location.GnssSignalQuality;
@@ -60,7 +61,6 @@
private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * 1e6;
private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * 1e6;
-
private long mLogStartInElapsedRealtimeMs;
GnssPowerMetrics mGnssPowerMetrics;
@@ -608,64 +608,72 @@
}
@Override
- public int onPullAtom(int atomTag, List<StatsEvent> data) {
- if (atomTag == FrameworkStatsLog.GNSS_STATS) {
- data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
- mLocationFailureReportsStatistics.getCount(),
- mLocationFailureReportsStatistics.getLongSum(),
- mTimeToFirstFixMilliSReportsStatistics.getCount(),
- mTimeToFirstFixMilliSReportsStatistics.getLongSum(),
- mPositionAccuracyMetersReportsStatistics.getCount(),
- mPositionAccuracyMetersReportsStatistics.getLongSum(),
- mTopFourAverageCn0DbmHzReportsStatistics.getCount(),
- mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(),
- mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(),
- mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
- mSvStatusReportsUsedInFix, mL5SvStatusReports,
- mL5SvStatusReportsUsedInFix));
- } else if (atomTag == FrameworkStatsLog.GNSS_POWER_STATS) {
- mGnssNative.requestPowerStats();
- GnssPowerStats gnssPowerStats = mGnssNative.getPowerStats();
- if (gnssPowerStats == null) {
- return StatsManager.PULL_SKIP;
- }
- double[] otherModesEnergyMilliJoule = new double[VENDOR_SPECIFIC_POWER_MODES_SIZE];
- double[] tempGnssPowerStatsOtherModes =
- gnssPowerStats.getOtherModesEnergyMilliJoule();
- if (tempGnssPowerStatsOtherModes.length < VENDOR_SPECIFIC_POWER_MODES_SIZE) {
- System.arraycopy(tempGnssPowerStatsOtherModes, 0,
- otherModesEnergyMilliJoule, 0,
- tempGnssPowerStatsOtherModes.length);
- } else {
- System.arraycopy(tempGnssPowerStatsOtherModes, 0,
- otherModesEnergyMilliJoule, 0,
- VENDOR_SPECIFIC_POWER_MODES_SIZE);
- }
- data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
- (long) (gnssPowerStats.getElapsedRealtimeUncertaintyNanos()),
- (long) (gnssPowerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO),
- (long) (gnssPowerStats.getSinglebandTrackingModeEnergyMilliJoule()
- * CONVERT_MILLI_TO_MICRO),
- (long) (gnssPowerStats.getMultibandTrackingModeEnergyMilliJoule()
- * CONVERT_MILLI_TO_MICRO),
- (long) (gnssPowerStats.getSinglebandAcquisitionModeEnergyMilliJoule()
- * CONVERT_MILLI_TO_MICRO),
- (long) (gnssPowerStats.getMultibandAcquisitionModeEnergyMilliJoule()
- * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[0] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[1] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[2] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[3] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[4] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[5] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[6] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[7] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[8] * CONVERT_MILLI_TO_MICRO),
- (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO)));
- } else {
- throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
+ public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+ switch (atomTag) {
+ case FrameworkStatsLog.GNSS_STATS:
+ return pullGnssStats(atomTag, data);
+ case FrameworkStatsLog.GNSS_POWER_STATS:
+ return pullGnssPowerStats(atomTag, data);
+ default:
+ throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
}
+ }
+ }
+
+ private int pullGnssStats(int atomTag, List<StatsEvent> data) {
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ mLocationFailureReportsStatistics.getCount(),
+ mLocationFailureReportsStatistics.getLongSum(),
+ mTimeToFirstFixMilliSReportsStatistics.getCount(),
+ mTimeToFirstFixMilliSReportsStatistics.getLongSum(),
+ mPositionAccuracyMetersReportsStatistics.getCount(),
+ mPositionAccuracyMetersReportsStatistics.getLongSum(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
+ mSvStatusReportsUsedInFix, mL5SvStatusReports,
+ mL5SvStatusReportsUsedInFix));
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private int pullGnssPowerStats(int atomTag, List<StatsEvent> data) {
+ GnssPowerStats powerStats = mGnssNative.requestPowerStatsBlocking();
+ if (powerStats == null) {
+ return StatsManager.PULL_SKIP;
+ } else {
+ data.add(createPowerStatsEvent(atomTag, powerStats));
return StatsManager.PULL_SUCCESS;
}
}
+
+ private static StatsEvent createPowerStatsEvent(int atomTag,
+ @NonNull GnssPowerStats powerStats) {
+ double[] otherModesEnergyMilliJoule = new double[VENDOR_SPECIFIC_POWER_MODES_SIZE];
+ double[] tempGnssPowerStatsOtherModes = powerStats.getOtherModesEnergyMilliJoule();
+ System.arraycopy(tempGnssPowerStatsOtherModes, 0,
+ otherModesEnergyMilliJoule, 0,
+ Math.min(tempGnssPowerStatsOtherModes.length, VENDOR_SPECIFIC_POWER_MODES_SIZE));
+ return FrameworkStatsLog.buildStatsEvent(atomTag,
+ (long) (powerStats.getElapsedRealtimeUncertaintyNanos()),
+ (long) (powerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO),
+ (long) (powerStats.getSinglebandTrackingModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (powerStats.getMultibandTrackingModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (powerStats.getSinglebandAcquisitionModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (powerStats.getMultibandAcquisitionModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[0] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[1] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[2] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[3] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[4] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[5] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[6] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[7] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[8] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO));
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index bdd4885..c79a21a 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -18,7 +18,9 @@
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
@@ -29,6 +31,7 @@
import android.location.GnssStatus;
import android.location.Location;
import android.os.Binder;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
@@ -46,9 +49,13 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Entry point for most GNSS HAL commands and callbacks.
@@ -140,6 +147,8 @@
public static final int AGPS_SETID_TYPE_IMSI = 1;
public static final int AGPS_SETID_TYPE_MSISDN = 2;
+ private static final int POWER_STATS_REQUEST_TIMEOUT_MILLIS = 100;
+
@IntDef(prefix = "AGPS_SETID_TYPE_", value = {AGPS_SETID_TYPE_NONE, AGPS_SETID_TYPE_IMSI,
AGPS_SETID_TYPE_MSISDN})
@Retention(RetentionPolicy.SOURCE)
@@ -289,6 +298,15 @@
byte responseType, boolean inEmergencyMode, boolean isCachedLocation);
}
+ /** Callback for reporting {@link GnssPowerStats} */
+ public interface PowerStatsCallback {
+ /**
+ * Called when power stats are reported.
+ * @param powerStats non-null value when power stats are available, {@code null} otherwise.
+ */
+ void onReportPowerStats(@Nullable GnssPowerStats powerStats);
+ }
+
// set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
// stops output right at 600m/s, depriving this of the information of a device that reaches
// greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
@@ -311,6 +329,8 @@
@GuardedBy("GnssNative.class")
private static GnssNative sInstance;
+ private final Handler mHandler;
+
/**
* Sets GnssHal instance to use for testing.
*/
@@ -367,6 +387,14 @@
private NavigationMessageCallbacks[] mNavigationMessageCallbacks =
new NavigationMessageCallbacks[0];
+ private @Nullable GnssPowerStats mLastKnownPowerStats = null;
+ private final Object mPowerStatsLock = new Object();
+ private final Runnable mPowerStatsTimeoutCallback = () -> {
+ Log.d(TAG, "Request for power stats timed out");
+ reportGnssPowerStats(null);
+ };
+ private final List<PowerStatsCallback> mPendingPowerStatsCallbacks = new ArrayList<>();
+
// these callbacks may only have a single implementation
private GeofenceCallbacks mGeofenceCallbacks;
private TimeCallbacks mTimeCallbacks;
@@ -381,7 +409,6 @@
private GnssCapabilities mCapabilities = new GnssCapabilities.Builder().build();
private @GnssCapabilities.TopHalCapabilityFlags int mTopFlags;
- private @Nullable GnssPowerStats mPowerStats = null;
private int mHardwareYear = 0;
private @Nullable String mHardwareModelName = null;
private long mStartRealtimeMs = 0;
@@ -391,6 +418,7 @@
mGnssHal = Objects.requireNonNull(gnssHal);
mEmergencyHelper = injector.getEmergencyHelper();
mConfiguration = configuration;
+ mHandler = FgThread.getHandler();
}
public void addBaseCallbacks(BaseCallbacks callbacks) {
@@ -532,8 +560,8 @@
/**
* Returns the latest power stats from the GNSS HAL.
*/
- public @Nullable GnssPowerStats getPowerStats() {
- return mPowerStats;
+ public @Nullable GnssPowerStats getLastKnownPowerStats() {
+ return mLastKnownPowerStats;
}
/**
@@ -931,10 +959,49 @@
/**
* Request an eventual update of GNSS power statistics.
+ *
+ * @param executor Executor that will run {@code callback}
+ * @param callback Called with non-null power stats if they were obtained in time, called with
+ * {@code null} if stats could not be obtained in time.
*/
- public void requestPowerStats() {
+ public void requestPowerStats(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull PowerStatsCallback callback) {
Preconditions.checkState(mRegistered);
- mGnssHal.requestPowerStats();
+ synchronized (mPowerStatsLock) {
+ mPendingPowerStatsCallbacks.add(powerStats -> {
+ Binder.withCleanCallingIdentity(
+ () -> executor.execute(() -> callback.onReportPowerStats(powerStats)));
+ });
+ if (mPendingPowerStatsCallbacks.size() == 1) {
+ mGnssHal.requestPowerStats();
+ mHandler.postDelayed(mPowerStatsTimeoutCallback,
+ POWER_STATS_REQUEST_TIMEOUT_MILLIS);
+ }
+ }
+ }
+
+ /**
+ * Request GNSS power statistics and blocks for a short time waiting for the result.
+ *
+ * @return non-null power stats, or {@code null} if stats could not be obtained in time.
+ */
+ public @Nullable GnssPowerStats requestPowerStatsBlocking() {
+ AtomicReference<GnssPowerStats> statsWrapper = new AtomicReference<>();
+ CountDownLatch latch = new CountDownLatch(1);
+ requestPowerStats(Runnable::run, powerStats -> {
+ statsWrapper.set(powerStats);
+ latch.countDown();
+ });
+
+ try {
+ latch.await(POWER_STATS_REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Interrupted while waiting for power stats");
+ Thread.currentThread().interrupt();
+ }
+
+ return statsWrapper.get();
}
/**
@@ -1167,7 +1234,14 @@
@NativeEntryPoint
void reportGnssPowerStats(GnssPowerStats powerStats) {
- mPowerStats = powerStats;
+ synchronized (mPowerStatsLock) {
+ mHandler.removeCallbacks(mPowerStatsTimeoutCallback);
+ if (powerStats != null) {
+ mLastKnownPowerStats = powerStats;
+ }
+ mPendingPowerStatsCallbacks.forEach(cb -> cb.onReportPowerStats(powerStats));
+ mPendingPowerStatsCallbacks.clear();
+ }
}
@NativeEntryPoint
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f65b79..c7b0f7d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7774,25 +7774,45 @@
}
private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
- if (removeRemoteView(pkg, tag, id, notification.contentView)) {
+ if (android.app.Flags.removeRemoteViews()) {
+ if (notification.contentView != null || notification.bigContentView != null
+ || notification.headsUpContentView != null
+ || (notification.publicVersion != null
+ && (notification.publicVersion.contentView != null
+ || notification.publicVersion.bigContentView != null
+ || notification.publicVersion.headsUpContentView != null))) {
+ Slog.i(TAG, "Removed customViews for " + pkg);
+ mUsageStats.registerImageRemoved(pkg);
+ }
notification.contentView = null;
- }
- if (removeRemoteView(pkg, tag, id, notification.bigContentView)) {
notification.bigContentView = null;
- }
- if (removeRemoteView(pkg, tag, id, notification.headsUpContentView)) {
notification.headsUpContentView = null;
- }
- if (notification.publicVersion != null) {
- if (removeRemoteView(pkg, tag, id, notification.publicVersion.contentView)) {
+ if (notification.publicVersion != null) {
notification.publicVersion.contentView = null;
- }
- if (removeRemoteView(pkg, tag, id, notification.publicVersion.bigContentView)) {
notification.publicVersion.bigContentView = null;
- }
- if (removeRemoteView(pkg, tag, id, notification.publicVersion.headsUpContentView)) {
notification.publicVersion.headsUpContentView = null;
}
+ } else {
+ if (removeRemoteView(pkg, tag, id, notification.contentView)) {
+ notification.contentView = null;
+ }
+ if (removeRemoteView(pkg, tag, id, notification.bigContentView)) {
+ notification.bigContentView = null;
+ }
+ if (removeRemoteView(pkg, tag, id, notification.headsUpContentView)) {
+ notification.headsUpContentView = null;
+ }
+ if (notification.publicVersion != null) {
+ if (removeRemoteView(pkg, tag, id, notification.publicVersion.contentView)) {
+ notification.publicVersion.contentView = null;
+ }
+ if (removeRemoteView(pkg, tag, id, notification.publicVersion.bigContentView)) {
+ notification.publicVersion.bigContentView = null;
+ }
+ if (removeRemoteView(pkg, tag, id, notification.publicVersion.headsUpContentView)) {
+ notification.publicVersion.headsUpContentView = null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index db18276..12e5180 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -341,10 +341,11 @@
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
- // must match: config_shortPressOnSettingsBehavior in config.xml
- static final int SHORT_PRESS_SETTINGS_NOTHING = 0;
- static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1;
- static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
+ // must match: config_settingsKeyBehavior in config.xml
+ static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
+ static final int SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1;
+ static final int SETTINGS_KEY_BEHAVIOR_NOTHING = 2;
+ static final int LAST_SETTINGS_KEY_BEHAVIOR = SETTINGS_KEY_BEHAVIOR_NOTHING;
static final int PENDING_KEY_NULL = -1;
@@ -370,8 +371,8 @@
static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1;
// Must match: config_searchKeyBehavior in config.xml
- static final int SEARCH_BEHAVIOR_DEFAULT_SEARCH = 0;
- static final int SEARCH_BEHAVIOR_TARGET_ACTIVITY = 1;
+ static final int SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH = 0;
+ static final int SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1;
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -643,8 +644,8 @@
// What we do when the user double-taps on home
int mDoubleTapOnHomeBehavior;
- // What we do when the user presses on settings
- int mShortPressOnSettingsBehavior;
+ // What we do when the user presses the settings key
+ int mSettingsKeyBehavior;
// Must match config_primaryShortPressTargetActivity in config.xml
ComponentName mPrimaryShortPressTargetActivity;
@@ -2858,11 +2859,11 @@
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
- mShortPressOnSettingsBehavior = res.getInteger(
- com.android.internal.R.integer.config_shortPressOnSettingsBehavior);
- if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING
- || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) {
- mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING;
+ mSettingsKeyBehavior = res.getInteger(
+ com.android.internal.R.integer.config_settingsKeyBehavior);
+ if (mSettingsKeyBehavior < SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY
+ || mSettingsKeyBehavior > LAST_SETTINGS_KEY_BEHAVIOR) {
+ mSettingsKeyBehavior = SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY;
}
}
@@ -3694,12 +3695,12 @@
case KeyEvent.KEYCODE_SEARCH:
if (firstDown && !keyguardOn) {
switch (mSearchKeyBehavior) {
- case SEARCH_BEHAVIOR_TARGET_ACTIVITY: {
+ case SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY: {
launchTargetSearchActivity();
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SEARCH);
return true;
}
- case SEARCH_BEHAVIOR_DEFAULT_SEARCH:
+ case SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH:
default:
break;
}
@@ -3778,14 +3779,16 @@
+ " interceptKeyBeforeQueueing");
return true;
case KeyEvent.KEYCODE_SETTINGS:
- if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) {
- if (!down) {
+ if (firstDown) {
+ if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
toggleNotificationPanel();
logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
+ } else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) {
+ showSystemSettings();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS);
}
- return true;
}
- break;
+ return true;
case KeyEvent.KEYCODE_STEM_PRIMARY:
if (prepareToSendSystemKeyToApplication(focusedToken, event)) {
// Send to app.
@@ -6563,8 +6566,8 @@
pw.print("mLongPressOnPowerBehavior=");
pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
pw.print(prefix);
- pw.print("mShortPressOnSettingsBehavior=");
- pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior));
+ pw.print("mSettingsKeyBehavior=");
+ pw.println(settingsKeyBehaviorToString(mSettingsKeyBehavior));
pw.print(prefix);
pw.print("mLongPressOnPowerAssistantTimeoutMs=");
pw.println(mLongPressOnPowerAssistantTimeoutMs);
@@ -6766,12 +6769,14 @@
}
}
- private static String shortPressOnSettingsBehaviorToString(int behavior) {
+ private static String settingsKeyBehaviorToString(int behavior) {
switch (behavior) {
- case SHORT_PRESS_SETTINGS_NOTHING:
- return "SHORT_PRESS_SETTINGS_NOTHING";
- case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL:
- return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL";
+ case SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY:
+ return "SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY";
+ case SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL:
+ return "SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL";
+ case SETTINGS_KEY_BEHAVIOR_NOTHING:
+ return "SETTINGS_KEY_BEHAVIOR_NOTHING";
default:
return Integer.toString(behavior);
}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index e1b4b88..fbdba4f 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -19,6 +19,7 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
+import android.os.BatteryStats;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
@@ -155,11 +156,17 @@
int powerComponentId = powerStats.descriptor.powerComponentId;
for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
if (stats.powerComponentId == powerComponentId) {
- stats.addPowerStats(powerStats, time);
+ stats.getConfig().getProcessor().addPowerStats(stats, powerStats, time);
}
}
}
+ public void noteStateChange(BatteryStats.HistoryItem item) {
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.getConfig().getProcessor().noteStateChange(stats, item);
+ }
+ }
+
void reset() {
mClockUpdates.clear();
mDurationMs = 0;
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 884c26c..1ff7cb7 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -205,7 +205,7 @@
private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
@Override
- void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
}
};
}
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
new file mode 100644
index 0000000..64c3446
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+class BinaryStatePowerStatsLayout extends PowerStatsLayout {
+ BinaryStatePowerStatsLayout() {
+ addDeviceSectionUsageDuration();
+ // Add a section for consumed energy, even if the specific device does not
+ // have support EnergyConsumers. This is done to guarantee format compatibility between
+ // PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
+ addDeviceSectionEnergyConsumers(1);
+ addDeviceSectionPowerEstimate();
+
+ addUidSectionUsageDuration();
+ addUidSectionPowerEstimate();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
new file mode 100644
index 0000000..490bd5e
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
@@ -0,0 +1,276 @@
+/*
+ * 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.power.stats;
+
+import android.annotation.IntDef;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.Process;
+
+import com.android.internal.os.PowerStats;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
+ static final int STATE_OFF = 0;
+ static final int STATE_ON = 1;
+
+ @IntDef(flag = true, prefix = {"STATE_"}, value = {
+ STATE_OFF,
+ STATE_ON,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ protected @interface BinaryState {
+ }
+
+ private final int mPowerComponentId;
+ private final PowerStatsUidResolver mUidResolver;
+ private final UsageBasedPowerEstimator mUsageBasedPowerEstimator;
+ private boolean mEnergyConsumerSupported;
+ private int mInitiatingUid = Process.INVALID_UID;
+ private @BinaryState int mLastState = STATE_OFF;
+ private long mLastStateTimestamp;
+ private long mLastUpdateTimestamp;
+
+ private PowerStats.Descriptor mDescriptor;
+ private final BinaryStatePowerStatsLayout mStatsLayout = new BinaryStatePowerStatsLayout();
+ private PowerStats mPowerStats;
+ private PowerEstimationPlan mPlan;
+ private long[] mTmpDeviceStatsArray;
+ private long[] mTmpUidStatsArray;
+
+ BinaryStatePowerStatsProcessor(int powerComponentId,
+ PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) {
+ mPowerComponentId = powerComponentId;
+ mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp);
+ mUidResolver = uidResolver;
+ }
+
+ protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item);
+
+ private void ensureInitialized() {
+ if (mDescriptor != null) {
+ return;
+ }
+
+ PersistableBundle extras = new PersistableBundle();
+ mStatsLayout.toExtras(extras);
+ mDescriptor = new PowerStats.Descriptor(mPowerComponentId,
+ mStatsLayout.getDeviceStatsArrayLength(), null, 0,
+ mStatsLayout.getUidStatsArrayLength(), extras);
+ mPowerStats = new PowerStats(mDescriptor);
+ mPowerStats.stats = new long[mDescriptor.statsArrayLength];
+ mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
+ mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
+ }
+
+ @Override
+ void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ ensureInitialized();
+
+ // Establish a baseline at the beginning of an accumulation pass
+ mLastState = STATE_OFF;
+ mLastStateTimestamp = timestampMs;
+ mInitiatingUid = Process.INVALID_UID;
+ flushPowerStats(stats, mLastStateTimestamp);
+ }
+
+ @Override
+ void noteStateChange(PowerComponentAggregatedPowerStats stats,
+ BatteryStats.HistoryItem item) {
+ @BinaryState int state = getBinaryState(item);
+ if (state == mLastState) {
+ return;
+ }
+
+ if (state == STATE_ON) {
+ if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_START)) {
+ mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
+ }
+ } else {
+ recordUsageDuration(item.time);
+ mInitiatingUid = Process.INVALID_UID;
+ if (!mEnergyConsumerSupported) {
+ flushPowerStats(stats, item.time);
+ }
+ }
+ mLastStateTimestamp = item.time;
+ mLastState = state;
+ }
+
+ private void recordUsageDuration(long time) {
+ if (mLastState == STATE_OFF) {
+ return;
+ }
+
+ long durationMs = time - mLastStateTimestamp;
+ mStatsLayout.setUsageDuration(mPowerStats.stats,
+ mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
+
+ if (mInitiatingUid != Process.INVALID_UID) {
+ long[] uidStats = mPowerStats.uidStats.get(mInitiatingUid);
+ if (uidStats == null) {
+ uidStats = new long[mDescriptor.uidStatsArrayLength];
+ mPowerStats.uidStats.put(mInitiatingUid, uidStats);
+ mStatsLayout.setUidUsageDuration(uidStats, durationMs);
+ } else {
+ mStatsLayout.setUsageDuration(mPowerStats.stats,
+ mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
+ }
+ }
+ mLastStateTimestamp = time;
+ }
+
+ void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
+ long timestampMs) {
+ ensureInitialized();
+ recordUsageDuration(timestampMs);
+ long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0);
+ if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) {
+ mEnergyConsumerSupported = true;
+ mStatsLayout.setConsumedEnergy(mPowerStats.stats, 0, consumedEnergy);
+ }
+
+ flushPowerStats(stats, timestampMs);
+ }
+
+ private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
+ mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+ stats.addPowerStats(mPowerStats, timestamp);
+
+ Arrays.fill(mPowerStats.stats, 0);
+ mPowerStats.uidStats.clear();
+ mLastUpdateTimestamp = timestamp;
+ }
+
+ private static class Intermediates {
+ public long duration;
+ public double power;
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ recordUsageDuration(timestampMs);
+ flushPowerStats(stats, timestampMs);
+
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ }
+
+ computeDevicePowerEstimates(stats);
+ combineDevicePowerEstimates(stats);
+
+ List<Integer> uids = new ArrayList<>();
+ stats.collectUids(uids);
+
+ computeUidActivityTotals(stats, uids);
+ computeUidPowerEstimates(stats, uids);
+ }
+
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ continue;
+ }
+
+ long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
+ if (duration > 0) {
+ double power;
+ if (mEnergyConsumerSupported) {
+ power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+ } else {
+ power = mUsageBasedPowerEstimator.calculatePower(duration);
+ }
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+ stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+ }
+ }
+ }
+
+ private void combineDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+ for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ CombinedDeviceStateEstimate estimation =
+ mPlan.combinedDeviceStateEstimations.get(i);
+ Intermediates intermediates = new Intermediates();
+ estimation.intermediates = intermediates;
+ for (int j = estimation.deviceStateEstimations.size() - 1; j >= 0; j--) {
+ DeviceStateEstimation deviceStateEstimation =
+ estimation.deviceStateEstimations.get(j);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray,
+ deviceStateEstimation.stateValues)) {
+ continue;
+ }
+ intermediates.power += mStatsLayout.getDevicePowerEstimate(mTmpDeviceStatsArray);
+ }
+ }
+ }
+
+ private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats,
+ List<Integer> uids) {
+ for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+ UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ for (int j = uids.size() - 1; j >= 0; j--) {
+ int uid = uids.get(j);
+ for (UidStateProportionalEstimate proportionalEstimate :
+ uidStateEstimate.proportionalEstimates) {
+ if (stats.getUidStats(mTmpUidStatsArray, uid,
+ proportionalEstimate.stateValues)) {
+ intermediates.duration +=
+ mStatsLayout.getUidUsageDuration(mTmpUidStatsArray);
+ }
+ }
+ }
+ }
+ }
+
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+ List<Integer> uids) {
+ for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+ UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ if (intermediates.duration == 0) {
+ continue;
+ }
+ List<UidStateProportionalEstimate> proportionalEstimates =
+ uidStateEstimate.proportionalEstimates;
+ for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+ UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+ for (int k = uids.size() - 1; k >= 0; k--) {
+ int uid = uids.get(k);
+ if (stats.getUidStats(mTmpUidStatsArray, uid,
+ proportionalEstimate.stateValues)) {
+ double power = intermediates.power
+ * mStatsLayout.getUidUsageDuration(mTmpUidStatsArray)
+ / intermediates.duration;
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues,
+ mTmpUidStatsArray);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
index 4d6db97..077b057 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
@@ -86,7 +86,7 @@
}
@Override
- void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
if (stats.getPowerStatsDescriptor() == null) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
index 57b7259..6da0a8f 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
@@ -138,7 +138,7 @@
}
@Override
- public void finish(PowerComponentAggregatedPowerStats stats) {
+ public void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
if (stats.getPowerStatsDescriptor() == null) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
index eebed2f..dcce562 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
@@ -166,7 +166,7 @@
}
@Override
- void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
if (stats.getPowerStatsDescriptor() == null) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
index a822281..c3a0aeb 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java
@@ -41,6 +41,7 @@
private static final String TAG = "MultiStateStats";
private static final String XML_TAG_STATS = "stats";
+ public static final int STATE_DOES_NOT_EXIST = -1;
/**
* A set of states, e.g. on-battery, screen-on, procstate. The state values are integers
@@ -70,6 +71,18 @@
}
/**
+ * Finds state by name in the provided array. If not found, returns STATE_DOES_NOT_EXIST.
+ */
+ public static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
+ for (int i = 0; i < states.length; i++) {
+ if (states[i].getName().equals(name)) {
+ return i;
+ }
+ }
+ return STATE_DOES_NOT_EXIST;
+ }
+
+ /**
* Iterates over all combinations of tracked states and invokes <code>consumer</code>
* for each of them.
*/
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
index 5c545fd..ec23fa0 100644
--- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
@@ -39,7 +39,7 @@
}
@Override
- void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
stats.setPowerStatsDescriptor(mDescriptor);
PowerComponentAggregatedPowerStats mobileRadioStats =
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 0528733..85a2293 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -16,6 +16,8 @@
package com.android.server.power.stats;
+import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.UserHandle;
@@ -68,10 +70,12 @@
private MultiStateStats mDeviceStats;
private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
+ private long[] mZeroArray;
private static class UidStats {
public int[] states;
public MultiStateStats stats;
+ public boolean updated;
}
PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
@@ -122,16 +126,18 @@
}
}
- if (mUidStateConfig[stateId].isTracked()) {
+ int uidStateId = MultiStateStats.States
+ .findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName());
+ if (uidStateId != STATE_DOES_NOT_EXIST && mUidStateConfig[uidStateId].isTracked()) {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
if (uidStats.stats == null) {
createUidStats(uidStats, timestampMs);
}
- uidStats.states[stateId] = state;
+ uidStats.states[uidStateId] = state;
if (uidStats.stats != null) {
- uidStats.stats.setState(stateId, state, timestampMs);
+ uidStats.stats.setState(uidStateId, state, timestampMs);
}
}
}
@@ -196,6 +202,22 @@
createUidStats(uidStats, timestampMs);
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
+ uidStats.updated = true;
+ }
+
+ // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment.
+ // It is essential to call `stats.increment(zero)` in order to record the new
+ // timestamp, which will ensure correct proportional attribution across all UIDs
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+ if (!uidStats.updated && uidStats.stats != null) {
+ if (mZeroArray == null
+ || mZeroArray.length != mPowerStatsDescriptor.uidStatsArrayLength) {
+ mZeroArray = new long[mPowerStatsDescriptor.uidStatsArrayLength];
+ }
+ uidStats.stats.increment(mZeroArray, timestampMs);
+ }
+ uidStats.updated = false;
}
mPowerStatsTimestamp = timestampMs;
@@ -217,10 +239,13 @@
uidStats = new UidStats();
uidStats.states = new int[mUidStateConfig.length];
for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
- if (mUidStateConfig[stateId].isTracked()
- && stateId < mDeviceStateConfig.length
- && mDeviceStateConfig[stateId].isTracked()) {
- uidStats.states[stateId] = mDeviceStates[stateId];
+ if (mUidStateConfig[stateId].isTracked()) {
+ int deviceStateId = MultiStateStats.States.findTrackedStateByName(
+ mDeviceStateConfig, mUidStateConfig[stateId].getName());
+ if (deviceStateId != STATE_DOES_NOT_EXIST
+ && mDeviceStateConfig[deviceStateId].isTracked()) {
+ uidStats.states[stateId] = mDeviceStates[deviceStateId];
+ }
}
}
mUidStats.put(uid, uidStats);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index 6a4c1f0..6e7fdf1 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -71,9 +71,13 @@
mStats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
}
+ start(mStats, startTimeMs);
+
boolean clockUpdateAdded = false;
long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
long lastTime = 0;
+ int lastStates = 0xFFFFFFFF;
+ int lastStates2 = 0xFFFFFFFF;
try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) {
while (iterator.hasNext()) {
BatteryStats.HistoryItem item = iterator.next();
@@ -111,6 +115,19 @@
mCurrentScreenState = screenState;
}
+ if ((item.states
+ & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES)
+ != lastStates
+ || (item.states2
+ & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2)
+ != lastStates2) {
+ mStats.noteStateChange(item);
+ lastStates = item.states
+ & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES;
+ lastStates2 = item.states2
+ & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2;
+ }
+
if (item.processStateChange != null) {
mStats.setUidState(item.processStateChange.uid,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
@@ -121,7 +138,7 @@
if (!mStats.isCompatible(item.powerStats)) {
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
- finish(mStats);
+ finish(mStats, lastTime);
consumer.accept(mStats);
}
mStats.reset();
@@ -134,7 +151,7 @@
}
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
- finish(mStats);
+ finish(mStats, lastTime);
consumer.accept(mStats);
}
@@ -142,12 +159,22 @@
}
}
- private void finish(AggregatedPowerStats stats) {
+ private void start(AggregatedPowerStats stats, long timestampMs) {
for (int i = 0; i < mProcessors.size(); i++) {
PowerComponentAggregatedPowerStats component =
stats.getPowerComponentStats(mProcessors.keyAt(i));
if (component != null) {
- mProcessors.valueAt(i).finish(component);
+ mProcessors.valueAt(i).start(component, timestampMs);
+ }
+ }
+ }
+
+ private void finish(AggregatedPowerStats stats, long timestampMs) {
+ for (int i = 0; i < mProcessors.size(); i++) {
+ PowerComponentAggregatedPowerStats component =
+ stats.getPowerComponentStats(mProcessors.keyAt(i));
+ if (component != null) {
+ mProcessors.valueAt(i).finish(component, timestampMs);
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
index 58efd94..9624fd2 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -31,6 +31,7 @@
private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+ private static final String EXTRA_UID_DURATION_POSITION = "ud";
private static final String EXTRA_UID_POWER_POSITION = "up";
protected static final int UNSUPPORTED = -1;
@@ -51,6 +52,7 @@
private int mDeviceEnergyConsumerPosition;
private int mDeviceEnergyConsumerCount;
private int mDevicePowerEstimatePosition = UNSUPPORTED;
+ private int mUidDurationPosition = UNSUPPORTED;
private int mUidPowerEstimatePosition = UNSUPPORTED;
public PowerStatsLayout() {
@@ -158,7 +160,8 @@
* PowerStatsService.
*/
public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy");
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy",
+ FLAG_OPTIONAL);
mDeviceEnergyConsumerCount = energyConsumerCount;
}
@@ -206,6 +209,13 @@
}
/**
+ * Declare that the UID stats array has a section capturing usage duration
+ */
+ public void addUidSectionUsageDuration() {
+ mUidDurationPosition = addUidSection(1, "time");
+ }
+
+ /**
* Declare that the UID stats array has a section capturing a power estimate
*/
public void addUidSectionPowerEstimate() {
@@ -220,6 +230,20 @@
}
/**
+ * Saves usage duration it in the corresponding element of <code>stats</code>.
+ */
+ public void setUidUsageDuration(long[] stats, long durationMs) {
+ stats[mUidDurationPosition] = durationMs;
+ }
+
+ /**
+ * Extracts the usage duration from a UID stats array.
+ */
+ public long getUidUsageDuration(long[] stats) {
+ return stats[mUidDurationPosition];
+ }
+
+ /**
* Converts the supplied mAh power estimate to a long and saves it in the corresponding
* element of <code>stats</code>.
*/
@@ -244,6 +268,7 @@
extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
mDeviceEnergyConsumerCount);
extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
@@ -258,6 +283,7 @@
mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 2fd0b9a..f257e1a 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -15,10 +15,16 @@
*/
package com.android.server.power.stats;
+import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
+import static com.android.server.power.stats.MultiStateStats.States.findTrackedStateByName;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.BatteryStats;
import android.util.Log;
+import com.android.internal.os.PowerStats;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -40,10 +46,21 @@
abstract class PowerStatsProcessor {
private static final String TAG = "PowerStatsProcessor";
- private static final int INDEX_DOES_NOT_EXIST = -1;
private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
- abstract void finish(PowerComponentAggregatedPowerStats stats);
+ void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ }
+
+ void noteStateChange(PowerComponentAggregatedPowerStats stats,
+ BatteryStats.HistoryItem item) {
+ }
+
+ void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
+ long timestampMs) {
+ stats.addPowerStats(powerStats, timestampMs);
+ }
+
+ abstract void finish(PowerComponentAggregatedPowerStats stats, long timestampMs);
protected static class PowerEstimationPlan {
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
@@ -79,7 +96,7 @@
}
int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName());
- if (index != INDEX_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
+ if (index != STATE_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
deviceStatesTrackedPerUid[i] = deviceStateConfig[i];
}
}
@@ -131,7 +148,7 @@
}
int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName());
- if (index != INDEX_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
+ if (index != STATE_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
uidStatesTrackedForDevice[i] = uidStateConfig[i];
} else {
uidStatesNotTrackedForDevice[i] = uidStateConfig[i];
@@ -303,15 +320,6 @@
}
}
- private static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
- for (int i = 0; i < states.length; i++) {
- if (states[i].getName().equals(name)) {
- return i;
- }
- }
- return INDEX_DOES_NOT_EXIST;
- }
-
@NonNull
private static String concatLabels(MultiStateStats.States[] config,
@AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
index a4a2e18..4e035c3 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
@@ -114,7 +114,7 @@
}
@Override
- void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
if (stats.getPowerStatsDescriptor() == null) {
return;
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f62c76e..e00e813 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -29,6 +29,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
+import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -47,6 +48,8 @@
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.location.ISignificantPlaceProvider;
+import android.hardware.location.ISignificantPlaceProviderManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -83,6 +86,8 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -248,6 +253,9 @@
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_SYSTEM;
+ private ServiceWatcher mSignificantPlaceServiceWatcher;
+ private volatile boolean mIsInSignificantPlace = false;
+
/**
* A class for providing dependencies to {@link TrustManagerService} in both production and test
* cases.
@@ -310,6 +318,38 @@
mTrustAgentsCanRun = true;
refreshAgentList(UserHandle.USER_ALL);
refreshDeviceLockedForUser(UserHandle.USER_ALL);
+
+ if (android.security.Flags.significantPlaces()) {
+ mSignificantPlaceServiceWatcher = ServiceWatcher.create(mContext, TAG,
+ CurrentUserServiceSupplier.create(
+ mContext,
+ TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER,
+ null,
+ null,
+ null),
+ new ServiceWatcher.ServiceListener<>() {
+ @Override
+ public void onBind(IBinder binder,
+ CurrentUserServiceSupplier.BoundServiceInfo service)
+ throws RemoteException {
+ ISignificantPlaceProvider.Stub.asInterface(binder)
+ .setSignificantPlaceProviderManager(
+ new ISignificantPlaceProviderManager.Stub() {
+ @Override
+ public void setInSignificantPlace(
+ boolean inSignificantPlace) {
+ mIsInSignificantPlace = inSignificantPlace;
+ }
+ });
+ }
+
+ @Override
+ public void onUnbind() {
+ mIsInSignificantPlace = false;
+ }
+ });
+ mSignificantPlaceServiceWatcher.register();
+ }
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
maybeEnableFactoryTrustAgents(UserHandle.USER_SYSTEM);
}
@@ -1651,6 +1691,11 @@
}
}
+ @Override
+ public boolean isInSignificantPlace() {
+ return mIsInSignificantPlace;
+ }
+
private void enforceReportPermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
@@ -1680,6 +1725,9 @@
for (UserInfo user : userInfos) {
dumpUser(fout, user, user.id == mCurrentUser);
}
+ if (mSignificantPlaceServiceWatcher != null) {
+ mSignificantPlaceServiceWatcher.dump(fout);
+ }
}
}, 1500);
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 110100a..8d14c1d 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -35,6 +35,7 @@
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
@@ -189,6 +190,9 @@
@Override
protected int getMaximumTemporaryServiceDurationMs() {
+ if (Build.isDebuggable()) {
+ return Integer.MAX_VALUE;
+ }
return MAX_TEMPORARY_SERVICE_DURATION_MS;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e0e61fd..eb6262c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -315,6 +315,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -356,6 +357,7 @@
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -8560,6 +8562,13 @@
if (isFixedOrientationLetterboxAllowed) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
+ // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+ // are already calculated in resolveFixedOrientationConfiguration.
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ if (!isLetterboxedForFixedOrientationAndAspectRatio()
+ && !mLetterboxUiController.hasFullscreenOverride()) {
+ resolveAspectRatioRestriction(newParentConfiguration);
+ }
final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets();
if (compatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration, compatDisplayInsets);
@@ -8572,14 +8581,6 @@
if (!matchParentBounds()) {
computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
}
- // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
- // are already calculated in resolveFixedOrientationConfiguration, or if in size compat
- // mode, it should already be calculated in resolveSizeCompatModeConfiguration.
- // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- }
- if (!isLetterboxedForFixedOrientationAndAspectRatio() && !mInSizeCompatModeForBounds
- && !mLetterboxUiController.hasFullscreenOverride()) {
- resolveAspectRatioRestriction(newParentConfiguration);
}
if (isFixedOrientationLetterboxAllowed || compatDisplayInsets != null
@@ -8801,7 +8802,7 @@
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
}
// Letterbox for limited aspect ratio.
- if (mIsAspectRatioApplied) {
+ if (isLetterboxedForAspectRatioOnly()) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
}
@@ -8830,13 +8831,27 @@
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
+ final boolean isImmersiveMode = isImmersiveMode(parentBounds);
+ final Insets navBarInsets;
+ if (isImmersiveMode) {
+ navBarInsets = mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().calculateInsets(
+ parentBounds,
+ WindowInsets.Type.navigationBars(),
+ true /* ignoreVisibility */);
+ } else {
+ navBarInsets = Insets.NONE;
+ }
// Horizontal position
int offsetX = 0;
if (parentBounds.width() != screenResolvedBoundsWidth) {
if (screenResolvedBoundsWidth <= parentAppBoundsWidth) {
float positionMultiplier = mLetterboxUiController.getHorizontalPositionMultiplier(
newParentConfiguration);
- offsetX = Math.max(0, (int) Math.ceil((parentAppBoundsWidth
+ // If in immersive mode, always align to right and overlap right insets (task bar)
+ // as they are transient and hidden. This removes awkward right spacing.
+ final int appWidth = (int) (parentAppBoundsWidth + navBarInsets.right);
+ offsetX = Math.max(0, (int) Math.ceil((appWidth
- screenResolvedBoundsWidth) * positionMultiplier)
// This is added to make sure that insets added inside
// CompatDisplayInsets#getContainerBounds() do not break the alignment
@@ -8856,9 +8871,8 @@
newParentConfiguration);
// If in immersive mode, always align to bottom and overlap bottom insets (nav bar,
// task bar) as they are transient and hidden. This removes awkward bottom spacing.
- final float newHeight = mDisplayContent.getDisplayPolicy().isImmersiveMode()
- ? parentBoundsHeight : parentAppBoundsHeight;
- offsetY = Math.max(0, (int) Math.ceil((newHeight
+ final int appHeight = (int) (parentAppBoundsHeight + navBarInsets.bottom);
+ offsetY = Math.max(0, (int) Math.ceil((appHeight
- screenResolvedBoundsHeight) * positionMultiplier)
// This is added to make sure that insets added inside
// CompatDisplayInsets#getContainerBounds() do not break the alignment
@@ -8878,7 +8892,8 @@
// If the top is aligned with parentAppBounds add the vertical insets back so that the app
// content aligns with the status bar
- if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top) {
+ if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top
+ && !isImmersiveMode) {
resolvedConfig.windowConfiguration.getBounds().top = parentBounds.top;
if (mSizeCompatBounds != null) {
mSizeCompatBounds.top = parentBounds.top;
@@ -8901,6 +8916,18 @@
}
}
+ boolean isImmersiveMode(@NonNull Rect parentBounds) {
+ if (!mResolveConfigHint.mUseOverrideInsetsForConfig) {
+ return false;
+ }
+ final Insets navBarInsets = mDisplayContent.getInsetsStateController()
+ .getRawInsetsState().calculateInsets(
+ parentBounds,
+ WindowInsets.Type.navigationBars(),
+ false /* ignoreVisibility */);
+ return Insets.NONE.equals(navBarInsets);
+ }
+
@NonNull Rect getScreenResolvedBounds() {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
@@ -8943,6 +8970,10 @@
return mLetterboxBoundsForFixedOrientationAndAspectRatio != null;
}
+ boolean isLetterboxedForAspectRatioOnly() {
+ return mLetterboxBoundsForAspectRatio != null;
+ }
+
boolean isAspectRatioApplied() {
return mIsAspectRatioApplied;
}
@@ -9235,11 +9266,11 @@
// orientation bounds (stored in resolved bounds) instead of parent bounds since the
// activity will be displayed within them even if it is in size compat mode. They should be
// saved here before resolved bounds are overridden below.
- final Rect containerBounds = isLetterboxedForFixedOrientationAndAspectRatio()
+ final Rect containerBounds = isAspectRatioApplied()
? new Rect(resolvedBounds)
: newParentConfiguration.windowConfiguration.getBounds();
- final Rect containerAppBounds = isLetterboxedForFixedOrientationAndAspectRatio()
- ? new Rect(getResolvedOverrideConfiguration().windowConfiguration.getAppBounds())
+ final Rect containerAppBounds = isAspectRatioApplied()
+ ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
: newParentConfiguration.windowConfiguration.getAppBounds();
final int requestedOrientation = getRequestedConfigurationOrientation();
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 2cccd33..1a9d211 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -56,6 +56,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,6 +76,7 @@
* is no guarantee that other system services are already present.
*/
class ActivityStartInterceptor {
+ private static final String TAG = "ActivityStartInterceptor";
private final ActivityTaskManagerService mService;
private final ActivityTaskSupervisor mSupervisor;
@@ -284,6 +286,8 @@
if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
return false;
}
+ Slog.i(TAG, "Intent : " + mIntent + " intercepted for user: " + mUserId
+ + " because quiet mode is enabled.");
IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index a9450c4..eb85c1a 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -505,10 +505,10 @@
*/
boolean shouldFreezeInsetsPosition(WindowState w) {
// Non-change transition (OP_APP_SWITCH) and METHOD_BLAST don't use screenshot so the
- // insets should keep original position before the start transaction is applied.
+ // insets should keep original position before the window is done with new rotation.
return mTransitionOp != OP_LEGACY && (isSeamlessTransition()
|| TransitionController.SYNC_METHOD == BLASTSyncEngine.METHOD_BLAST)
- && !mIsStartTransactionCommitted && canBeAsync(w.mToken) && isTargetToken(w.mToken);
+ && canBeAsync(w.mToken) && isTargetToken(w.mToken);
}
/** Returns true if there won't be a screen rotation animation (screenshot-based). */
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 1128328..50ac801 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -18,8 +18,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.LaunchParamsModifierUtils.applyLayoutGravity;
-import static com.android.server.wm.LaunchParamsModifierUtils.calculateLayoutBounds;
+import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity;
+import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/wm/LaunchParamsModifierUtils.java b/services/core/java/com/android/server/wm/LaunchParamsModifierUtils.java
deleted file mode 100644
index db54647..0000000
--- a/services/core/java/com/android/server/wm/LaunchParamsModifierUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
-import android.util.Size;
-import android.view.Gravity;
-
-class LaunchParamsModifierUtils {
-
- /**
- * Calculates bounds based on window layout size manifest values. These can include width,
- * height, width fraction and height fraction. In the event only one dimension of values are
- * specified in the manifest (e.g. width but no height value), the corresponding display area
- * dimension will be used as the default value unless some desired sizes have been specified.
- */
- static void calculateLayoutBounds(@NonNull Rect stableBounds,
- @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds,
- @Nullable Size desiredSize) {
- final int defaultWidth = stableBounds.width();
- final int defaultHeight = stableBounds.height();
- int width;
- int height;
-
- if (desiredSize == null) {
- // If desired bounds have not been specified, use the exiting default bounds as the
- // desired.
- desiredSize = new Size(stableBounds.width(), stableBounds.height());
- }
-
- width = desiredSize.getWidth();
- if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
- width = windowLayout.width;
- } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
- width = (int) (defaultWidth * windowLayout.widthFraction);
- }
-
- height = desiredSize.getHeight();
- if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
- height = windowLayout.height;
- } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
- height = (int) (defaultHeight * windowLayout.heightFraction);
- }
-
- inOutBounds.set(0, 0, width, height);
- }
-
- /**
- * Applies a vertical and horizontal gravity on the inOutBounds in relation to the stableBounds.
- */
- static void applyLayoutGravity(int verticalGravity, int horizontalGravity,
- @NonNull Rect inOutBounds, @NonNull Rect stableBounds) {
- final int width = inOutBounds.width();
- final int height = inOutBounds.height();
-
- final float fractionOfHorizontalOffset;
- switch (horizontalGravity) {
- case Gravity.LEFT:
- fractionOfHorizontalOffset = 0f;
- break;
- case Gravity.RIGHT:
- fractionOfHorizontalOffset = 1f;
- break;
- default:
- fractionOfHorizontalOffset = 0.5f;
- }
-
- final float fractionOfVerticalOffset;
- switch (verticalGravity) {
- case Gravity.TOP:
- fractionOfVerticalOffset = 0f;
- break;
- case Gravity.BOTTOM:
- fractionOfVerticalOffset = 1f;
- break;
- default:
- fractionOfVerticalOffset = 0.5f;
- }
-
- inOutBounds.offsetTo(stableBounds.left, stableBounds.top);
- final int xOffset = (int) (fractionOfHorizontalOffset * (stableBounds.width() - width));
- final int yOffset = (int) (fractionOfVerticalOffset * (stableBounds.height() - height));
- inOutBounds.offset(xOffset, yOffset);
- }
-}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsUtil.java b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
index cd071af..9416daf 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsUtil.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
@@ -23,9 +23,11 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.util.Size;
+import android.view.Gravity;
import android.view.View;
/**
@@ -195,4 +197,79 @@
}
inOutBounds.offset(dx, dy);
}
+
+ /**
+ * Calculates bounds based on window layout size manifest values. These can include width,
+ * height, width fraction and height fraction. In the event only one dimension of values are
+ * specified in the manifest (e.g. width but no height value), the corresponding display area
+ * dimension will be used as the default value unless some desired sizes have been specified.
+ */
+ static void calculateLayoutBounds(@NonNull Rect stableBounds,
+ @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds,
+ @Nullable Size desiredSize) {
+ final int defaultWidth = stableBounds.width();
+ final int defaultHeight = stableBounds.height();
+ int width;
+ int height;
+
+ if (desiredSize == null) {
+ // If desired bounds have not been specified, use the exiting default bounds as the
+ // desired.
+ desiredSize = new Size(stableBounds.width(), stableBounds.height());
+ }
+
+ width = desiredSize.getWidth();
+ if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
+ width = windowLayout.width;
+ } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
+ width = (int) (defaultWidth * windowLayout.widthFraction);
+ }
+
+ height = desiredSize.getHeight();
+ if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
+ height = windowLayout.height;
+ } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
+ height = (int) (defaultHeight * windowLayout.heightFraction);
+ }
+
+ inOutBounds.set(0, 0, width, height);
+ }
+
+ /**
+ * Applies a vertical and horizontal gravity on the inOutBounds in relation to the stableBounds.
+ */
+ static void applyLayoutGravity(int verticalGravity, int horizontalGravity,
+ @NonNull Rect inOutBounds, @NonNull Rect stableBounds) {
+ final int width = inOutBounds.width();
+ final int height = inOutBounds.height();
+
+ final float fractionOfHorizontalOffset;
+ switch (horizontalGravity) {
+ case Gravity.LEFT:
+ fractionOfHorizontalOffset = 0f;
+ break;
+ case Gravity.RIGHT:
+ fractionOfHorizontalOffset = 1f;
+ break;
+ default:
+ fractionOfHorizontalOffset = 0.5f;
+ }
+
+ final float fractionOfVerticalOffset;
+ switch (verticalGravity) {
+ case Gravity.TOP:
+ fractionOfVerticalOffset = 0f;
+ break;
+ case Gravity.BOTTOM:
+ fractionOfVerticalOffset = 1f;
+ break;
+ default:
+ fractionOfVerticalOffset = 0.5f;
+ }
+
+ inOutBounds.offsetTo(stableBounds.left, stableBounds.top);
+ final int xOffset = (int) (fractionOfHorizontalOffset * (stableBounds.width() - width));
+ final int yOffset = (int) (fractionOfVerticalOffset * (stableBounds.height() - height));
+ inOutBounds.offset(xOffset, yOffset);
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 6e11e08..b8d0694 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1704,7 +1704,7 @@
if (mainWin.isLetterboxedForDisplayCutout()) {
return "DISPLAY_CUTOUT";
}
- if (mActivityRecord.isAspectRatioApplied()) {
+ if (mActivityRecord.isLetterboxedForAspectRatioOnly()) {
return "ASPECT_RATIO";
}
return "UNKNOWN_REASON";
diff --git a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
index 3606a34..69be0d9 100644
--- a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
+++ b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT;
+import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP;
import android.annotation.NonNull;
import android.internal.perfetto.protos.ShellTransitionOuterClass.ShellTransition;
@@ -44,7 +44,7 @@
DataSourceParams params =
new DataSourceParams.Builder()
.setBufferExhaustedPolicy(
- PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
.build();
mDataSource.register(params);
}
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index 1007357..b7944d3 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -107,11 +107,12 @@
void onConfigurationChanged() {
final Resources r = mContext.getResources();
- final int defaultThreshold = r.getDimensionPixelSize(
+ final int startThreshold = r.getDimensionPixelSize(
com.android.internal.R.dimen.system_gestures_start_threshold);
- mSwipeStartThreshold.set(defaultThreshold, defaultThreshold, defaultThreshold,
- defaultThreshold);
- mSwipeDistanceThreshold = defaultThreshold;
+ mSwipeStartThreshold.set(startThreshold, startThreshold, startThreshold,
+ startThreshold);
+ mSwipeDistanceThreshold = r.getDimensionPixelSize(
+ com.android.internal.R.dimen.system_gestures_distance_threshold);
final Display display = DisplayManagerGlobal.getInstance()
.getRealDisplay(Display.DEFAULT_DISPLAY);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f37638f..5c9a84d 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -40,8 +40,6 @@
import static com.android.server.wm.ActivityStarter.Request;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.LaunchParamsModifierUtils.applyLayoutGravity;
-import static com.android.server.wm.LaunchParamsModifierUtils.calculateLayoutBounds;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -644,13 +642,13 @@
displayArea.getStableRect(stableBounds);
if (windowLayout.hasSpecifiedSize()) {
- calculateLayoutBounds(stableBounds, windowLayout, inOutBounds,
+ LaunchParamsUtil.calculateLayoutBounds(stableBounds, windowLayout, inOutBounds,
/* desiredBounds */ null);
} else if (inOutBounds.isEmpty()) {
getTaskBounds(root, displayArea, windowLayout, WINDOWING_MODE_FREEFORM,
/* hasInitialBounds */ false, inOutBounds);
}
- applyLayoutGravity(verticalGravity, horizontalGravity, inOutBounds,
+ LaunchParamsUtil.applyLayoutGravity(verticalGravity, horizontalGravity, inOutBounds,
stableBounds);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d733762..e763c9e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import static android.app.admin.DevicePolicyIdentifiers.PACKAGES_SUSPENDED_POLICY;
import static android.app.admin.DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_TARGET_USER_ID;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY;
@@ -78,6 +79,10 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -260,9 +265,7 @@
boolean policyEnforced = Objects.equals(
localPolicyState.getCurrentResolvedPolicy(), value);
// TODO(b/285532044): remove hack and handle properly
- if (!policyEnforced
- && policyDefinition.getPolicyKey().getIdentifier().equals(
- USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+ if (!policyEnforced && shouldApplyPackageSetUnionPolicyHack(policyDefinition)) {
PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
PolicyValue<Set<String>> parsedResolvedValue =
(PolicyValue<Set<String>>) localPolicyState.getCurrentResolvedPolicy();
@@ -528,8 +531,7 @@
globalPolicyState.getCurrentResolvedPolicy(), value);
// TODO(b/285532044): remove hack and handle properly
if (!policyAppliedGlobally
- && policyDefinition.getPolicyKey().getIdentifier().equals(
- USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+ && shouldApplyPackageSetUnionPolicyHack(policyDefinition)) {
PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
PolicyValue<Set<String>> parsedResolvedValue =
(PolicyValue<Set<String>>) globalPolicyState.getCurrentResolvedPolicy();
@@ -666,8 +668,7 @@
}
// TODO(b/285532044): remove hack and handle properly
- if (policyDefinition.getPolicyKey().getIdentifier().equals(
- USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+ if (shouldApplyPackageSetUnionPolicyHack(policyDefinition)) {
if (!Objects.equals(value, localPolicyState.getCurrentResolvedPolicy())) {
PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
PolicyValue<Set<String>> parsedResolvedValue =
@@ -688,6 +689,12 @@
*/
@Nullable
<V> V getResolvedPolicy(@NonNull PolicyDefinition<V> policyDefinition, int userId) {
+ PolicyValue<V> resolvedValue = getResolvedPolicyValue(policyDefinition, userId);
+ return resolvedValue == null ? null : resolvedValue.getValue();
+ }
+
+ private <V> PolicyValue<V> getResolvedPolicyValue(@NonNull PolicyDefinition<V> policyDefinition,
+ int userId) {
Objects.requireNonNull(policyDefinition);
synchronized (mLock) {
@@ -699,11 +706,39 @@
resolvedValue = getGlobalPolicyStateLocked(
policyDefinition).getCurrentResolvedPolicy();
}
- return resolvedValue == null ? null : resolvedValue.getValue();
+ return resolvedValue;
}
}
/**
+ * Retrieves resolved policy for the provided {@code policyDefinition} and a list of
+ * users.
+ */
+ @Nullable
+ <V> V getResolvedPolicyAcrossUsers(@NonNull PolicyDefinition<V> policyDefinition,
+ List<Integer> users) {
+ Objects.requireNonNull(policyDefinition);
+
+ List<PolicyValue<V>> adminPolicies = new ArrayList<>();
+ synchronized (mLock) {
+ for (int userId : users) {
+ PolicyValue<V> resolvedValue = getResolvedPolicyValue(policyDefinition, userId);
+ if (resolvedValue != null) {
+ adminPolicies.add(resolvedValue);
+ }
+ }
+ }
+ // We will be aggregating PolicyValue across multiple admins across multiple users,
+ // including different policies set by the same admin on different users. This is
+ // not supported by ResolutionMechanism generically, instead we need to call the special
+ // resolve() method that doesn't care about admins who set the policy. Note that not every
+ // ResolutionMechanism supports this.
+ PolicyValue<V> resolvedValue =
+ policyDefinition.getResolutionMechanism().resolve(adminPolicies);
+ return resolvedValue == null ? null : resolvedValue.getValue();
+ }
+
+ /**
* Retrieves the policy set by the admin for the provided {@code policyDefinition} and
* {@code userId} if one was set, otherwise returns {@code null}.
*/
@@ -1743,6 +1778,18 @@
}
}
+ /**
+ * Create a backup of the policy engine XML file, so that we can recover previous state
+ * in case some data-loss bug is triggered e.g. during migration.
+ *
+ * Backup is only created if one with the same ID does not exist yet.
+ */
+ void createBackup(String backupId) {
+ synchronized (mLock) {
+ DevicePoliciesReaderWriter.createBackup(backupId);
+ }
+ }
+
<V> void reapplyAllPoliciesOnBootLocked() {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
@@ -1820,8 +1867,22 @@
return false;
}
+ /**
+ * For PackageSetUnion policies, we can't simply compare the resolved policy against the admin's
+ * policy for equality to determine if the admin has applied the policy successfully, instead
+ * the admin's policy should be considered applied successfully as long as its policy is subset
+ * of the resolved policy. This method controls which policies should use this special logic.
+ */
+ private <V> boolean shouldApplyPackageSetUnionPolicyHack(PolicyDefinition<V> policy) {
+ String policyKey = policy.getPolicyKey().getIdentifier();
+ return policyKey.equals(USER_CONTROL_DISABLED_PACKAGES_POLICY)
+ || policyKey.equals(PACKAGES_SUSPENDED_POLICY);
+ }
+
private class DevicePoliciesReaderWriter {
private static final String DEVICE_POLICIES_XML = "device_policy_state.xml";
+ private static final String BACKUP_DIRECTORY = "device_policy_backups";
+ private static final String BACKUP_FILENAME = "device_policy_state.%s.xml";
private static final String TAG_LOCAL_POLICY_ENTRY = "local-policy-entry";
private static final String TAG_GLOBAL_POLICY_ENTRY = "global-policy-entry";
private static final String TAG_POLICY_STATE_ENTRY = "policy-state-entry";
@@ -1836,8 +1897,30 @@
private final File mFile;
+ private static File getFileName() {
+ return new File(Environment.getDataSystemDirectory(), DEVICE_POLICIES_XML);
+ }
private DevicePoliciesReaderWriter() {
- mFile = new File(Environment.getDataSystemDirectory(), DEVICE_POLICIES_XML);
+ mFile = getFileName();
+ }
+
+ public static void createBackup(String backupId) {
+ try {
+ File backupDirectory = new File(Environment.getDataSystemDirectory(),
+ BACKUP_DIRECTORY);
+ backupDirectory.mkdir();
+ Path backupPath = Path.of(backupDirectory.getPath(),
+ BACKUP_FILENAME.formatted(backupId));
+ if (backupPath.toFile().exists()) {
+ Log.w(TAG, "Backup already exist: " + backupPath);
+ } else {
+ Files.copy(getFileName().toPath(), backupPath,
+ StandardCopyOption.REPLACE_EXISTING);
+ Log.i(TAG, "Backup created at " + backupPath);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot create backup " + backupId, e);
+ }
}
void writeToFileLocked() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5bf5efd..bdd0730 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -569,6 +569,7 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -2221,32 +2222,6 @@
return packageNameAndSignature;
}
- private void unsuspendAppsForQuietProfiles() {
- PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
- List<UserInfo> users = mUserManagerInternal.getUsers(true /* excludeDying */);
-
- for (UserInfo user : users) {
- if (!user.isManagedProfile() || !user.isQuietModeEnabled()) {
- continue;
- }
- int userId = user.id;
- var suspendedByAdmin = getPackagesSuspendedByAdmin(userId);
- var packagesToUnsuspend = mInjector.getPackageManager(userId)
- .getInstalledPackages(PackageManager.PackageInfoFlags.of(
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE))
- .stream()
- .map(packageInfo -> packageInfo.packageName)
- .filter(pkg -> !suspendedByAdmin.contains(pkg))
- .toArray(String[]::new);
-
- Slogf.i(LOG_TAG, "Unsuspending work apps for user %d", userId);
- // When app suspension was used for quiet mode, the apps were suspended by platform
- // package, just like when admin suspends them. So although it wasn't admin who
- // suspended, this method will remove the right suspension record.
- pmi.setPackagesSuspendedByAdmin(userId, packagesToUnsuspend, false /* suspended */);
- }
- }
-
private Owners makeOwners(Injector injector, PolicyPathProvider pathProvider) {
return new Owners(
injector.getUserManager(), injector.getUserManagerInternal(),
@@ -3470,6 +3445,79 @@
new BooleanPolicyValue(true));
}
+ @GuardedBy("getLockObject()")
+ private boolean maybeMigrateRequiredPasswordComplexityLocked(String backupId) {
+ Slog.i(LOG_TAG, "Migrating password complexity to policy engine");
+ if (!Flags.unmanagedModeMigration()) {
+ return false;
+ }
+ if (mOwners.isRequiredPasswordComplexityMigrated()) {
+ return false;
+ }
+ // Create backup if none exists
+ mDevicePolicyEngine.createBackup(backupId);
+ try {
+ iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> {
+ int userId = enforcingAdmin.getUserId();
+ if (admin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ enforcingAdmin,
+ new IntegerPolicyValue(admin.mPasswordComplexity),
+ userId);
+ }
+ ActiveAdmin parentAdmin = admin.getParentActiveAdmin();
+ if (parentAdmin != null
+ && parentAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ enforcingAdmin,
+ new IntegerPolicyValue(parentAdmin.mPasswordComplexity),
+ getProfileParentId(userId));
+ }
+ });
+ } catch (Exception e) {
+ Slog.wtf(LOG_TAG, "Failed to migrate password complexity to policy engine", e);
+ }
+
+ Slog.i(LOG_TAG, "Marking password complexity migration complete");
+ mOwners.markRequiredPasswordComplexityMigrated();
+ return true;
+ }
+
+ @GuardedBy("getLockObject()")
+ private boolean maybeMigrateSuspendedPackagesLocked(String backupId) {
+ Slog.i(LOG_TAG, "Migrating suspended packages to policy engine");
+ if (!Flags.unmanagedModeMigration()) {
+ return false;
+ }
+ if (mOwners.isSuspendedPackagesMigrated()) {
+ return false;
+ }
+ // Create backup if none exists
+ mDevicePolicyEngine.createBackup(backupId);
+ try {
+ iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> {
+ if (admin.suspendedPackages == null || admin.suspendedPackages.size() == 0) {
+ return;
+ }
+ int userId = enforcingAdmin.getUserId();
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PACKAGES_SUSPENDED,
+ enforcingAdmin,
+ new PackageSetPolicyValue(new ArraySet<>(admin.suspendedPackages)),
+ userId);
+ });
+ } catch (Exception e) {
+ Slog.wtf(LOG_TAG, "Failed to migrate suspended packages to policy engine", e);
+ }
+
+ Slog.i(LOG_TAG, "Marking suspended packages migration complete");
+ mOwners.markSuspendedPackagesMigrated();
+ return true;
+ }
+
+
private void applyManagedSubscriptionsPolicyIfRequired() {
int copeProfileUserId = getOrganizationOwnedProfileUserId();
// This policy is relevant only for COPE devices.
@@ -4254,27 +4302,50 @@
}
final int userId = caller.getUserId();
+ EnforcingAdmin enforcingAdmin = null;
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
+ if (Flags.unmanagedModeMigration()) {
+ enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(who,
+ userId,
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD));
+ }
// If setPasswordQuality is called on the parent, ensure that
// the primary admin does not have password complexity state (this is an
// unsupported state).
if (parent) {
- final ActiveAdmin primaryAdmin = getActiveAdminForCallerLocked(
- who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, false);
- final boolean hasComplexitySet =
- primaryAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE;
+ final boolean hasComplexitySet;
+ if (Flags.unmanagedModeMigration()) {
+ Integer complexity = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ enforcingAdmin,
+ userId);
+ hasComplexitySet = complexity != null && complexity != PASSWORD_COMPLEXITY_NONE;
+ } else {
+ final ActiveAdmin primaryAdmin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, false);
+ hasComplexitySet = primaryAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE;
+ }
+
Preconditions.checkState(!hasComplexitySet,
"Cannot set password quality when complexity is set on the primary admin."
+ " Set the primary admin's complexity to NONE first.");
}
+ final EnforcingAdmin enforcingAdminFinal = enforcingAdmin;
mInjector.binderWithCleanCallingIdentity(() -> {
final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
if (passwordPolicy.quality != quality) {
passwordPolicy.quality = quality;
- ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
+ if (Flags.unmanagedModeMigration()) {
+ int affectedUser = parent ? getProfileParentId(userId) : userId;
+ mDevicePolicyEngine.removeLocalPolicy(PolicyDefinition.PASSWORD_COMPLEXITY,
+ enforcingAdminFinal, affectedUser);
+ } else {
+ ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
+ }
resetInactivePasswordRequirementsIfRPlus(userId, ap);
updatePasswordValidityCheckpointLocked(userId, parent);
updatePasswordQualityCacheForUserGroup(userId);
@@ -4405,6 +4476,10 @@
}
}
+ /**
+ * @deprecated use {@link #getResolvedLockscreenPolicy(PolicyDefinition, int)} for
+ * coexistable policies
+ */
@GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
if (isSeparateProfileChallengeEnabled(userHandle)) {
@@ -4428,6 +4503,25 @@
}
/**
+ * Returns a user's resolved lockscreen policy from all admins. This is different from normal
+ * policy resolution because if the specified user has a work profile with unified challenge,
+ * all policies set on the profile will also affect that user.
+ */
+ private <V> V getResolvedLockscreenPolicy(PolicyDefinition<V> policyDefinition, int userId) {
+ if (isSeparateProfileChallengeEnabled(userId)) {
+ // If this profile has a separate challenge, only return policies targeting itself.
+ return mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+ }
+ // Otherwise, this user is either a full user, or it's a profile with unified challenge.
+ // In both cases we query the parent user who owns the credential (the parent user of a full
+ // user is itself), plus any profile of the parent user who has unified challenge since
+ // the policy of a unified challenge profile is enforced on the parent.
+ return getResolvedPolicyForUserAndItsManagedProfiles(policyDefinition,
+ getProfileParentId(userId),
+ (user) -> mLockPatternUtils.isProfileWithUnifiedChallenge(user.id));
+
+ }
+ /**
* Get the list of active admins for an affected user:
* <ul>
* <li>The active admins associated with the userHandle itself</li>
@@ -4460,6 +4554,9 @@
* profile associated with the given user. Optionally also include the admin of each managed
* profile.
* <p> Should not be called on a profile user.
+ *
+ * For coexistable policy, please use
+ * {@link #getResolvedPolicyForUserAndItsManagedProfiles(PolicyDefinition, int, Predicate)}
*/
@GuardedBy("getLockObject()")
private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesLocked(int userHandle,
@@ -4486,6 +4583,23 @@
return admins;
}
+ private <V> V getResolvedPolicyForUserAndItsManagedProfiles(
+ PolicyDefinition<V> policyDefinition, int userHandle,
+ Predicate<UserInfo> shouldIncludeProfile) {
+ List<Integer> users = new ArrayList<>();
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+ if (userInfo.id == userHandle) {
+ users.add(userInfo.id);
+ } else if (userInfo.isManagedProfile() && shouldIncludeProfile.test(userInfo)) {
+ users.add(userInfo.id);
+ }
+ }
+ });
+ return mDevicePolicyEngine.getResolvedPolicyAcrossUsers(policyDefinition, users);
+ }
+
/**
* Returns the list of admins on the given user, as well as parent admins for each managed
* profile associated with the given user. Optionally also include the admin of each managed
@@ -5266,13 +5380,30 @@
/* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser
|| !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size());
- int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
- for (ActiveAdmin admin : admins) {
- adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
- maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
+ if (Flags.unmanagedModeMigration()) {
+ for (ActiveAdmin admin: admins) {
+ adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+ }
+ Integer maxRequiredComplexity = getResolvedPolicyForUserAndItsManagedProfiles(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ userHandle,
+ /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser
+ || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+ return PasswordMetrics.validatePasswordMetrics(
+ PasswordMetrics.merge(adminMetrics),
+ maxRequiredComplexity != null
+ ? maxRequiredComplexity : PASSWORD_COMPLEXITY_NONE,
+ metrics).isEmpty();
+ } else {
+ int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
+ for (ActiveAdmin admin : admins) {
+ adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+ maxRequiredComplexity = Math.max(maxRequiredComplexity,
+ admin.mPasswordComplexity);
+ }
+ return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
+ maxRequiredComplexity, metrics).isEmpty();
}
- return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
- maxRequiredComplexity, metrics).isEmpty();
}
}
@@ -5363,6 +5494,76 @@
Preconditions.checkArgument(allowedModes.contains(passwordComplexity),
"Provided complexity is not one of the allowed values.");
+ if (!Flags.unmanagedModeMigration()) {
+ setRequiredPasswordComplexityPreCoexistence(callerPackageName, passwordComplexity,
+ calledOnParent);
+ return;
+ }
+
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ int affectedUser = calledOnParent
+ ? getProfileParentId(caller.getUserId()) : caller.getUserId();
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(null,
+ MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, caller.getPackageName(),
+ caller.getUserId());
+ Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
+
+ ActiveAdmin activeAdmin = admin.getActiveAdmin();
+
+ // We require the caller to explicitly clear any password quality requirements set
+ // on the parent DPM instance, to avoid the case where password requirements are
+ // specified in the form of quality on the parent but complexity on the profile
+ // itself.
+ if (!calledOnParent) {
+ final boolean hasQualityRequirementsOnParent = activeAdmin.hasParentActiveAdmin()
+ && activeAdmin.getParentActiveAdmin().mPasswordPolicy.quality
+ != PASSWORD_QUALITY_UNSPECIFIED;
+ Preconditions.checkState(!hasQualityRequirementsOnParent,
+ "Password quality is set on the parent when attempting to set password"
+ + "complexity. Clear the quality by setting the password quality "
+ + "on the parent to PASSWORD_QUALITY_UNSPECIFIED first");
+ }
+
+ if (passwordComplexity != PASSWORD_COMPLEXITY_NONE) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ admin,
+ new IntegerPolicyValue(passwordComplexity),
+ affectedUser);
+ } else {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ admin,
+ affectedUser);
+ }
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ // Reset the password policy.
+ if (calledOnParent) {
+ activeAdmin.getParentActiveAdmin().mPasswordPolicy = new PasswordPolicy();
+ } else {
+ activeAdmin.mPasswordPolicy = new PasswordPolicy();
+ }
+ synchronized (getLockObject()) {
+ updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent);
+ }
+ updatePasswordQualityCacheForUserGroup(caller.getUserId());
+ saveSettingsLocked(caller.getUserId());
+ });
+
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY)
+ .setAdmin(caller.getPackageName())
+ .setInt(passwordComplexity)
+ .setBoolean(calledOnParent)
+ .write();
+ logPasswordComplexityRequiredIfSecurityLogEnabled(caller.getPackageName(),
+ caller.getUserId(), calledOnParent, passwordComplexity);
+ }
+
+ private void setRequiredPasswordComplexityPreCoexistence(
+ String callerPackageName, int passwordComplexity, boolean calledOnParent) {
CallerIdentity caller = getCallerIdentity(callerPackageName);
if (!isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(
@@ -5441,6 +5642,30 @@
@GuardedBy("getLockObject()")
private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle,
boolean deviceWideOnly) {
+ if (Flags.unmanagedModeMigration()) {
+ return getAggregatedPasswordComplexity(userHandle, deviceWideOnly);
+ } else {
+ return getAggregatedPasswordComplexityPreCoexistenceLocked(userHandle, deviceWideOnly);
+ }
+ }
+
+ private int getAggregatedPasswordComplexity(@UserIdInt int userHandle, boolean deviceWideOnly) {
+ ensureLocked();
+ Integer result;
+ if (deviceWideOnly) {
+ result = getResolvedPolicyForUserAndItsManagedProfiles(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ userHandle,
+ /* shouldIncludeProfile */ (user) -> false);
+ } else {
+ result = getResolvedLockscreenPolicy(PolicyDefinition.PASSWORD_COMPLEXITY, userHandle);
+ }
+ return result != null ? result : PASSWORD_COMPLEXITY_NONE;
+ }
+
+ @GuardedBy("getLockObject()")
+ private int getAggregatedPasswordComplexityPreCoexistenceLocked(@UserIdInt int userHandle,
+ boolean deviceWideOnly) {
ensureLocked();
final List<ActiveAdmin> admins;
if (deviceWideOnly) {
@@ -5462,24 +5687,30 @@
return PASSWORD_COMPLEXITY_NONE;
}
- final CallerIdentity caller = getCallerIdentity();
-
- if (isPermissionCheckFlagEnabled()) {
+ if (Flags.unmanagedModeMigration()) {
+ final CallerIdentity caller = getCallerIdentity(callerPackageName);
int affectedUser = calledOnParent ? getProfileParentId(caller.getUserId())
: caller.getUserId();
enforcePermission(MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
callerPackageName, affectedUser);
+
+ Integer complexity = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PASSWORD_COMPLEXITY,
+ affectedUser);
+ return complexity != null ? complexity : PASSWORD_COMPLEXITY_NONE;
} else {
+ final CallerIdentity caller = getCallerIdentity();
+
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwner(caller));
Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller));
- }
- synchronized (getLockObject()) {
- final ActiveAdmin requiredAdmin = getParentOfAdminIfRequired(
- getDeviceOrProfileOwnerAdminLocked(caller.getUserId()), calledOnParent);
- return requiredAdmin.mPasswordComplexity;
+ synchronized (getLockObject()) {
+ final ActiveAdmin requiredAdmin = getParentOfAdminIfRequired(
+ getDeviceOrProfileOwnerAdminLocked(caller.getUserId()), calledOnParent);
+ return requiredAdmin.mPasswordComplexity;
+ }
}
}
@@ -9288,6 +9519,10 @@
}
}
+ // TODO: with a quick glance this logic seems incomplete that it doesn't properly handle
+ // the different behaviour between a profile with separate challenge vs a profile with
+ // unified challenge, which was part of getActiveAdminsForLockscreenPoliciesLocked()
+ // before the migration.
if (isUnicornFlagEnabled()) {
Integer features = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.KEYGUARD_DISABLED_FEATURES,
@@ -12983,8 +13218,7 @@
return result;
}
- @Override
- public String[] setPackagesSuspended(ComponentName who, String callerPackage,
+ private String[] setPackagesSuspendedPreCoexistence(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
ActiveAdmin admin;
@@ -13065,6 +13299,78 @@
return result;
}
+ @Override
+ public String[] setPackagesSuspended(ComponentName who, String callerPackage,
+ String[] packageNames, boolean suspended) {
+ if (!Flags.unmanagedModeMigration()) {
+ return setPackagesSuspendedPreCoexistence(who, callerPackage, packageNames, suspended);
+ }
+
+ final CallerIdentity caller = getCallerIdentity(who, callerPackage);
+
+ EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ who,
+ MANAGE_DEVICE_POLICY_PACKAGE_STATE,
+ caller.getPackageName(),
+ caller.getUserId());
+ checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED);
+
+ Set<String> packages = new ArraySet<>(packageNames);
+ Set<String> suspendedPackagesBefore = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PACKAGES_SUSPENDED, caller.getUserId());
+
+ Set<String> currentPackages = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+ PolicyDefinition.PACKAGES_SUSPENDED,
+ enforcingAdmin,
+ caller.getUserId());
+ if (currentPackages == null) currentPackages = new ArraySet<>();
+ if (suspended) {
+ currentPackages.addAll(packages);
+ } else {
+ currentPackages.removeAll(packages);
+ }
+ if (currentPackages.isEmpty()) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PACKAGES_SUSPENDED,
+ enforcingAdmin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PACKAGES_SUSPENDED,
+ enforcingAdmin,
+ new PackageSetPolicyValue(currentPackages),
+ caller.getUserId());
+ }
+
+ Set<String> suspendedPackagesAfter = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PACKAGES_SUSPENDED, caller.getUserId());
+
+ PackageSuspender suspender = new PackageSuspender(
+ suspendedPackagesBefore, suspendedPackagesAfter,
+ listPolicyExemptAppsUnchecked(mContext),
+ mInjector.getPackageManagerInternal(), caller.getUserId());
+
+ String[] result;
+ synchronized (getLockObject()) {
+ long id = mInjector.binderClearCallingIdentity();
+ try {
+ result = suspended ? suspender.suspend(packages) : suspender.unsuspend(packages);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PACKAGES_SUSPENDED)
+ .setAdmin(caller.getPackageName())
+ .setBoolean(/* isDelegate */ who == null)
+ .setStrings(packageNames)
+ .write();
+
+ if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Returning %s", Arrays.toString(result));
+ return result;
+ }
+
/**
* Returns an array containing the union of the given non-suspended packages and
* exempt apps. Assumes both parameters are non-null and non-empty.
@@ -13086,7 +13392,7 @@
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (isUnicornFlagEnabled()) {
+ if (Flags.unmanagedModeMigration()) {
enforcePermission(
MANAGE_DEVICE_POLICY_PACKAGE_STATE,
caller.getPackageName(),
@@ -15257,17 +15563,6 @@
}
}
- private Set<String> getPackagesSuspendedByAdmin(@UserIdInt int userId) {
- synchronized (getLockObject()) {
- ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
- if (admin == null || admin.suspendedPackages == null) {
- return Collections.emptySet();
- } else {
- return new ArraySet<>(admin.suspendedPackages);
- }
- }
- }
-
/**
* We need to update the internal state of whether a user has completed setup or a
* device has paired once. After that, we ignore any changes that reset the
@@ -23523,7 +23818,39 @@
}
Slog.w(LOG_TAG, "Work apps may have been paused via suspension previously.");
- unsuspendAppsForQuietProfiles();
+ PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ List<UserInfo> users = mUserManagerInternal.getUsers(true /* excludeDying */);
+
+ for (UserInfo user : users) {
+ if (!user.isManagedProfile() || !user.isQuietModeEnabled()) {
+ continue;
+ }
+ int userId = user.id;
+ Set<String> suspendedByAdmin;
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userId);
+ // This is legacy code from Turn off Work 2.0 which is before setPackagesSuspended
+ // is migrated to PolicyEngine, so we only need to query the legacy ActiveAdmin here
+ if (admin == null || admin.suspendedPackages == null) {
+ suspendedByAdmin = Collections.emptySet();
+ } else {
+ suspendedByAdmin = new ArraySet<>(admin.suspendedPackages);
+ }
+ }
+ var packagesToUnsuspend = mInjector.getPackageManager(userId)
+ .getInstalledPackages(PackageManager.PackageInfoFlags.of(
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE))
+ .stream()
+ .map(packageInfo -> packageInfo.packageName)
+ .filter(pkg -> !suspendedByAdmin.contains(pkg))
+ .toArray(String[]::new);
+
+ Slogf.i(LOG_TAG, "Unsuspending work apps for user %d", userId);
+ // When app suspension was used for quiet mode, the apps were suspended by platform
+ // package, just like when admin suspends them. So although it wasn't admin who
+ // suspended, this method will remove the right suspension record.
+ pmi.setPackagesSuspendedByAdmin(userId, packagesToUnsuspend, false /* suspended */);
+ }
}
public void setMtePolicy(int flags, String callerPackageName) {
@@ -23947,6 +24274,15 @@
@GuardedBy("getLockObject()")
private void migratePoliciesToPolicyEngineLocked() {
maybeMigrateSecurityLoggingPolicyLocked();
+ // ID format: <sdk-int>.<auto_increment_id>.<descriptions>'
+ String unmanagedBackupId = "35.1.unmanaged-mode";
+ boolean migrated = false;
+ migrated = migrated | maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
+ migrated = migrated | maybeMigrateSuspendedPackagesLocked(unmanagedBackupId);
+ if (migrated) {
+ Slogf.i(LOG_TAG, "Backup made: " + unmanagedBackupId);
+ }
+ // Additional migration steps should repeat the pattern above with a new backupId.
}
private void migrateAutoTimezonePolicy() {
@@ -24211,6 +24547,23 @@
});
}
+ @GuardedBy("getLockObject()")
+ private void iterateThroughDpcAdminsLocked(BiConsumer<ActiveAdmin, EnforcingAdmin> runner) {
+ Binder.withCleanCallingIdentity(() -> {
+ List<UserInfo> users = mUserManager.getUsers();
+ for (UserInfo userInfo : users) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(userInfo.id);
+ if (admin == null) continue;
+ EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(),
+ userInfo.id,
+ admin);
+
+ runner.accept(admin, enforcingAdmin);
+ }
+ });
+ }
+
private List<PackageInfo> getInstalledPackagesOnUser(int userId) {
return mInjector.binderWithCleanCallingIdentity(() ->
mContext.getPackageManager().getInstalledPackagesAsUser(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
index 7e8eaa7..2066376 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/MostRestrictive.java
@@ -19,9 +19,9 @@
import android.annotation.NonNull;
import android.app.admin.PolicyValue;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
final class MostRestrictive<V> extends ResolutionMechanism<V> {
@@ -33,18 +33,21 @@
@Override
PolicyValue<V> resolve(@NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies) {
+ return resolve(new ArrayList<>(adminPolicies.values()));
+ }
+
+ @Override
+ PolicyValue<V> resolve(List<PolicyValue<V>> adminPolicies) {
if (adminPolicies.isEmpty()) {
return null;
}
for (PolicyValue<V> value : mMostToLeastRestrictive) {
- if (adminPolicies.containsValue(value)) {
+ if (adminPolicies.contains(value)) {
return value;
}
}
// Return first set policy if none can be found in known values
- Map.Entry<EnforcingAdmin, PolicyValue<V>> policy =
- adminPolicies.entrySet().stream().findFirst().get();
- return policy.getValue();
+ return adminPolicies.get(0);
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 7912cbc..3f9605a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -636,6 +636,33 @@
}
}
+ boolean isRequiredPasswordComplexityMigrated() {
+ synchronized (mData) {
+ return mData.mRequiredPasswordComplexityMigrated;
+ }
+ }
+
+ void markRequiredPasswordComplexityMigrated() {
+ synchronized (mData) {
+ mData.mRequiredPasswordComplexityMigrated = true;
+ mData.writeDeviceOwner();
+ }
+
+ }
+
+ boolean isSuspendedPackagesMigrated() {
+ synchronized (mData) {
+ return mData.mSuspendedPackagesMigrated;
+ }
+ }
+
+ void markSuspendedPackagesMigrated() {
+ synchronized (mData) {
+ mData.mSuspendedPackagesMigrated = true;
+ mData.writeDeviceOwner();
+ }
+ }
+
boolean isMigratedPostUpdate() {
synchronized (mData) {
return mData.mPoliciesMigratedPostUpdate;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index d02cfee..2ea5f16 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -88,7 +88,9 @@
private static final String ATTR_MIGRATED_TO_POLICY_ENGINE = "migratedToPolicyEngine";
private static final String ATTR_SECURITY_LOG_MIGRATED = "securityLogMigrated";
-
+ private static final String ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED =
+ "passwordComplexityMigrated";
+ private static final String ATTR_SUSPENDED_PACKAGES_MIGRATED = "suspendedPackagesMigrated";
private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade";
// Internal state for the device owner package.
@@ -118,6 +120,8 @@
boolean mMigratedToPolicyEngine = false;
boolean mSecurityLoggingMigrated = false;
+ boolean mRequiredPasswordComplexityMigrated = false;
+ boolean mSuspendedPackagesMigrated = false;
boolean mPoliciesMigratedPostUpdate = false;
@@ -409,6 +413,14 @@
if (Flags.securityLogV2Enabled()) {
out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
}
+ if (Flags.unmanagedModeMigration()) {
+ out.attributeBoolean(null, ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED,
+ mRequiredPasswordComplexityMigrated);
+ out.attributeBoolean(null, ATTR_SUSPENDED_PACKAGES_MIGRATED,
+ mSuspendedPackagesMigrated);
+
+ }
+
out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
}
@@ -473,6 +485,12 @@
null, ATTR_MIGRATED_POST_UPGRADE, false);
mSecurityLoggingMigrated = Flags.securityLogV2Enabled()
&& parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
+ mRequiredPasswordComplexityMigrated = Flags.unmanagedModeMigration()
+ && parser.getAttributeBoolean(null,
+ ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED, false);
+ mSuspendedPackagesMigrated = Flags.unmanagedModeMigration()
+ && parser.getAttributeBoolean(null,
+ ATTR_SUSPENDED_PACKAGES_MIGRATED, false);
break;
default:
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PackageSuspender.java b/services/devicepolicy/java/com/android/server/devicepolicy/PackageSuspender.java
new file mode 100644
index 0000000..40cf0e9
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PackageSuspender.java
@@ -0,0 +1,155 @@
+/*
+ * 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.devicepolicy;
+
+import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageManagerInternal;
+import android.util.ArraySet;
+
+import com.android.server.utils.Slogf;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class for calling into PackageManagerInternal.setPackagesSuspendedByAdmin.
+ * Two main things this class encapsulates:
+ * 1. Handling of the DPM internal suspension exemption list
+ * 2. Calculating the failed packages result in the context of coexistence
+ *
+ * 1 is handled by the two internal methods {@link #suspendWithExemption(Set)} and
+ * {@link #unsuspendWithExemption(Set)} where the exemption list is taken into consideration
+ * before and after calling {@link PackageManagerInternal#setPackagesSuspendedByAdmin}.
+ * In order to compute 2, the resolved package suspension state before and after suspension is
+ * needed as multiple admins can both suspend the same packages under coexistence.
+ */
+public class PackageSuspender {
+
+ private final Set<String> mSuspendedPackageBefore;
+ private final Set<String> mSuspendedPackageAfter;
+ private final List<String> mExemptedPackages;
+ private final PackageManagerInternal mPackageManager;
+ private final int mUserId;
+
+ public PackageSuspender(@Nullable Set<String> suspendedPackageBefore,
+ @Nullable Set<String> suspendedPackageAfter, List<String> exemptedPackages,
+ PackageManagerInternal pmi, int userId) {
+ mSuspendedPackageBefore =
+ suspendedPackageBefore != null ? suspendedPackageBefore : Collections.emptySet();
+ mSuspendedPackageAfter =
+ suspendedPackageAfter != null ? suspendedPackageAfter : Collections.emptySet();
+ mExemptedPackages = exemptedPackages;
+ mPackageManager = pmi;
+ mUserId = userId;
+ }
+
+ /**
+ * Suspend packages that are requested by a single admin
+ *
+ * @return a list of packages that the admin has requested to suspend but could not be
+ * suspended, due to DPM and PackageManager exemption list.
+ *
+ */
+ public String[] suspend(Set<String> packages) {
+ // When suspending, call PM with the list of packages admin has requested, even if some
+ // of these packages are already in suspension (some other admin might have already
+ // suspended them). We do it this way so that we can simply return the failed list from
+ // PackageManager to the caller as the accurate list of unsuspended packages.
+ // This is different from the unsuspend() logic, please see below.
+ //
+ // For example admin A already suspended package 1, 2 and 3, but package 3 is
+ // PackageManager-exempted. Now admin B wants to suspend package 2, 3 and 4 (2 and 4 are
+ // suspendable). We need to return package 3 as the unsuspended package here, and we ask
+ // PackageManager to suspend package 2, 3 and 4 here (who will return package 3 in the
+ // failed list, and package 2 is already suspended).
+ Set<String> result = suspendWithExemption(packages);
+ return result.toArray(String[]::new);
+ }
+
+ /**
+ * Suspend packages considering the exemption list.
+ *
+ * @return the list of packages that couldn't be suspended, either due to the exemption list,
+ * or due to failures from PackageManagerInternal itself.
+ */
+ private Set<String> suspendWithExemption(Set<String> packages) {
+ Set<String> packagesToSuspend = new ArraySet<>(packages);
+ // Any original packages that are also in the exempted list will not be suspended and hence
+ // will appear in the final result.
+ Set<String> result = new ArraySet<>(mExemptedPackages);
+ result.retainAll(packagesToSuspend);
+ // Remove exempted packages before calling PackageManager
+ packagesToSuspend.removeAll(mExemptedPackages);
+ String[] failedPackages = mPackageManager.setPackagesSuspendedByAdmin(
+ mUserId, packagesToSuspend.toArray(String[]::new), true);
+ if (failedPackages == null) {
+ Slogf.w(LOG_TAG, "PM failed to suspend packages (%s)", packages);
+ return packages;
+ } else {
+ result.addAll(Arrays.asList(failedPackages));
+ return result;
+ }
+ }
+
+ /**
+ * Unsuspend packages that are requested by a single admin
+ *
+ * @return a list of packages that the admin has requested to unsuspend but could not be
+ * unsuspended, due to other amdin's policy or PackageManager restriction.
+ *
+ */
+ public String[] unsuspend(Set<String> packages) {
+ // Unlike suspend(), when unsuspending, call PackageManager with the delta of resolved
+ // suspended packages list and not what the admin has requested. This is because some
+ // packages might still be subject to another admin's suspension request.
+ Set<String> packagesToUnsuspend = new ArraySet<>(mSuspendedPackageBefore);
+ packagesToUnsuspend.removeAll(mSuspendedPackageAfter);
+
+ // To calculate the result (which packages are not unsuspended), start with packages that
+ // are still subject to another admin's suspension policy. This is calculated by
+ // intersecting the packages argument with mSuspendedPackageAfter.
+ Set<String> result = new ArraySet<>(packages);
+ result.retainAll(mSuspendedPackageAfter);
+ // Remove mExemptedPackages since they can't be suspended to start with.
+ result.removeAll(mExemptedPackages);
+ // Finally make the unsuspend() request and add packages that PackageManager can't unsuspend
+ // to the result.
+ result.addAll(unsuspendWithExemption(packagesToUnsuspend));
+ return result.toArray(String[]::new);
+ }
+
+ /**
+ * Unsuspend packages considering the exemption list.
+ *
+ * @return the list of packages that couldn't be unsuspended, either due to the exemption list,
+ * or due to failures from PackageManagerInternal itself.
+ */
+ private Set<String> unsuspendWithExemption(Set<String> packages) {
+ // when unsuspending, no need to consider exemption list since by definition they can't
+ // be suspended to begin with.
+ String[] failedPackages = mPackageManager.setPackagesSuspendedByAdmin(
+ mUserId, packages.toArray(String[]::new), false);
+ if (failedPackages == null) {
+ Slogf.w(LOG_TAG, "PM failed to unsuspend packages (%s)", packages);
+ }
+ return new ArraySet<>(failedPackages);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 8bec384..a0ea4e9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -240,7 +240,7 @@
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE
| POLICY_FLAG_NON_COEXISTABLE_POLICY
| POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED,
- PolicyEnforcerCallbacks::setApplicationRestrictions,
+ PolicyEnforcerCallbacks::noOp,
new BundlePolicySerializer());
/**
@@ -263,7 +263,7 @@
new MostRecent<>(),
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
// DevicePolicyManagerService handles the enforcement, this just takes care of storage
- (Long value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ PolicyEnforcerCallbacks::noOp,
new LongPolicySerializer());
static PolicyDefinition<Integer> KEYGUARD_DISABLED_FEATURES = new PolicyDefinition<>(
@@ -271,7 +271,7 @@
new FlagUnion(),
POLICY_FLAG_LOCAL_ONLY_POLICY,
// Nothing is enforced for keyguard features, we just need to store it
- (Integer value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ PolicyEnforcerCallbacks::noOp,
new IntegerPolicySerializer());
// This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
@@ -312,7 +312,7 @@
TRUE_MORE_RESTRICTIVE,
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
// Nothing is enforced, we just need to store it
- (Boolean value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ PolicyEnforcerCallbacks::noOp,
new BooleanPolicySerializer());
/**
@@ -332,7 +332,7 @@
new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
new MostRecent<>(),
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
- (Set<String> value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
@@ -366,6 +366,30 @@
PolicyEnforcerCallbacks::setContentProtectionPolicy,
new IntegerPolicySerializer());
+ static PolicyDefinition<Integer> PASSWORD_COMPLEXITY = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.PASSWORD_COMPLEXITY_POLICY),
+ new MostRestrictive<>(
+ List.of(
+ new IntegerPolicyValue(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH),
+ new IntegerPolicyValue(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM),
+ new IntegerPolicyValue(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_LOW),
+ new IntegerPolicyValue(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_NONE))),
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ PolicyEnforcerCallbacks::noOp,
+ new IntegerPolicySerializer());
+
+ static PolicyDefinition<Set<String>> PACKAGES_SUSPENDED =
+ new PolicyDefinition<>(
+ new NoArgsPolicyKey(
+ DevicePolicyIdentifiers.PACKAGES_SUSPENDED_POLICY),
+ new PackageSetUnion(),
+ PolicyEnforcerCallbacks::noOp,
+ new PackageSetPolicySerializer());
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -405,6 +429,13 @@
USB_DATA_SIGNALING);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY,
CONTENT_PROTECTION);
+ // Intentionally not flagged since if the flag is flipped off on a device already
+ // having PASSWORD_COMPLEXITY policy in the on-device XML, it will cause the
+ // deserialization logic to break due to seeing an unknown tag.
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PASSWORD_COMPLEXITY_POLICY,
+ PASSWORD_COMPLEXITY);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PACKAGES_SUSPENDED_POLICY,
+ PACKAGES_SUSPENDED);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
@@ -523,7 +554,7 @@
private final PolicyKey mPolicyKey;
private final ResolutionMechanism<V> mResolutionMechanism;
private final int mPolicyFlags;
- // A function that accepts policy to apple, context, userId, callback arguments, and returns
+ // A function that accepts policy to apply, context, userId, callback arguments, and returns
// true if the policy has been enforced successfully.
private final QuadFunction<V, Context, Integer, PolicyKey, Boolean> mPolicyEnforcerCallback;
private final PolicySerializer<V> mPolicySerializer;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 04d277e..e1cb37d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -73,6 +73,10 @@
private static final String LOG_TAG = "PolicyEnforcerCallbacks";
+ static <T> boolean noOp(T value, Context context, Integer userId, PolicyKey policyKey) {
+ return true;
+ }
+
static boolean setAutoTimezoneEnabled(@Nullable Boolean enabled, @NonNull Context context) {
if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index c544ebc..245c438 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -200,7 +200,7 @@
pw.println(mPolicyDefinition.getPolicyKey());
pw.increaseIndent();
- pw.println("Per-admin Policy");
+ pw.println("Per-admin Policy:");
pw.increaseIndent();
if (mPoliciesSetByAdmins.size() == 0) {
pw.println("null");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java b/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
index c321aa1..ad7ac2b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ResolutionMechanism.java
@@ -20,9 +20,24 @@
import android.app.admin.PolicyValue;
import java.util.LinkedHashMap;
+import java.util.List;
abstract class ResolutionMechanism<V> {
+ /**
+ * The most generic resolution logic where we know both the policy value and the admin who
+ * sets it.
+ */
@Nullable
abstract PolicyValue<V> resolve(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminPolicies);
+
+ /**
+ * A special resolution logic that does not care about admins who set them. Only applicable to
+ * a subset of ResolutionMechanism.
+ */
+ @Nullable
+ PolicyValue<V> resolve(List<PolicyValue<V>> adminPolicies) {
+ throw new UnsupportedOperationException();
+ }
+
abstract android.app.admin.ResolutionMechanism<V> getParcelableResolutionMechanism();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe4e17..927df8b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -761,6 +761,9 @@
}
}
+ private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
+ private long mBinderCallbackLast = -1;
+
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
@@ -965,6 +968,14 @@
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/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 3628a57..d3efcb6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -16,7 +16,6 @@
package com.android.server.display;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
@@ -44,6 +43,7 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.test.TestLooper;
import android.util.SparseArray;
import android.view.Display;
@@ -54,6 +54,7 @@
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.OffsettableClock;
import org.junit.After;
@@ -68,6 +69,8 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
+ private static final int ANDROID_SLEEP_TIME = 1000;
+ private static final int NANO_SECONDS_MULTIPLIER = 1000000;
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
private static final int LIGHT_SENSOR_RATE = 20;
@@ -100,6 +103,8 @@
@Mock BrightnessRangeController mBrightnessRangeController;
@Mock
BrightnessClamperController mBrightnessClamperController;
+ @Mock
+ DisplayManagerFlags mDisplayManagerFlags;
@Mock BrightnessThrottler mBrightnessThrottler;
@Before
@@ -148,8 +153,18 @@
}
@Override
- AutomaticBrightnessController.Clock createClock() {
- return mClock::now;
+ AutomaticBrightnessController.Clock createClock(boolean isEnabled) {
+ return new AutomaticBrightnessController.Clock() {
+ @Override
+ public long uptimeMillis() {
+ return mClock.now();
+ }
+
+ @Override
+ public long getSensorEventScaleTime() {
+ return mClock.now() + ANDROID_SLEEP_TIME;
+ }
+ };
}
}, // pass in test looper instead, pass in offsettable clock
@@ -166,7 +181,7 @@
mContext, mBrightnessRangeController, mBrightnessThrottler,
useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
- mBrightnessClamperController
+ mBrightnessClamperController, mDisplayManagerFlags
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -350,7 +365,7 @@
when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
when(mBrightnessMappingStrategy.shouldResetShortTermModel(
123f, 0.5f)).thenReturn(true);
@@ -360,7 +375,7 @@
mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
mTestLooper.dispatchAll();
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
mTestLooper.moveTimeForward(4000);
mTestLooper.dispatchAll();
@@ -394,14 +409,14 @@
when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f);
when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
// Time does not move forward, since clock is doesn't increment naturally.
mTestLooper.dispatchAll();
// Sensor reads 100000 lux,
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910));
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
// Verify short term model is not reset.
verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
@@ -432,7 +447,7 @@
when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
@@ -446,7 +461,7 @@
mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
mTestLooper.dispatchAll();
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
mTestLooper.moveTimeForward(4000);
mTestLooper.dispatchAll();
@@ -479,7 +494,7 @@
when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
@@ -493,7 +508,7 @@
// Do not fast-forward time.
mTestLooper.dispatchAll();
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
// Do not fast-forward time
mTestLooper.dispatchAll();
@@ -523,7 +538,7 @@
// No user brightness interaction.
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
PowerManager.BRIGHTNESS_INVALID_FLOAT);
when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
@@ -534,7 +549,7 @@
// Do not fast-forward time.
mTestLooper.dispatchAll();
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
// Do not fast-forward time
mTestLooper.dispatchAll();
@@ -568,7 +583,7 @@
verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt());
// Now let's do the same for idle mode
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
// Called once when switching,
// setAmbientLux() is called twice and once in updateAutoBrightness(),
// nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
@@ -800,6 +815,43 @@
}
@Test
+ public void testAmbientLuxBuffers_prunedBeyondLongHorizonExceptLatestValue() throws Exception {
+ when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
+ int increment = 11;
+ int lux = 5000;
+ for (int i = 0; i < 1000; i++) {
+ lux += increment;
+ mClock.fastForward(increment);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
+ }
+ mClock.fastForward(AMBIENT_LIGHT_HORIZON_LONG + 10);
+ int newLux = 2000;
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, newLux,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
+
+ float[] sensorValues = mController.getLastSensorValues();
+ long[] sensorTimestamps = mController.getLastSensorTimestamps();
+ // Only the values within the horizon should be kept
+ assertEquals(2, sensorValues.length);
+ assertEquals(2, sensorTimestamps.length);
+
+ assertEquals(lux, sensorValues[0], EPSILON);
+ assertEquals(newLux, sensorValues[1], EPSILON);
+ assertEquals(mClock.now() + ANDROID_SLEEP_TIME - AMBIENT_LIGHT_HORIZON_LONG,
+ sensorTimestamps[0]);
+ assertEquals(mClock.now() + ANDROID_SLEEP_TIME,
+ sensorTimestamps[1]);
+ }
+
+ @Test
public void testGetSensorReadingsFullBuffer() throws Exception {
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -966,7 +1018,7 @@
BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
/* useHorizon= */ false);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -1003,7 +1055,7 @@
BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
/* useHorizon= */ false);
- mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -1030,35 +1082,6 @@
}
@Test
- public void testBrightnessBasedOnLastUsedLux() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- // Set up system to return 0.3f as a brightness value
- float lux = 100.0f;
- // Brightness as float (from 0.0f to 1.0f)
- float normalizedBrightness = 0.3f;
- when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
- when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
- when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
- /* category= */ anyInt())).thenReturn(normalizedBrightness);
- when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
-
- // Send a new sensor value, disable the sensor and verify
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
- mController.configure(AUTO_BRIGHTNESS_DISABLED, /* configuration= */ null,
- /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
- /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
- /* shouldResetShortTermModel= */ true);
- assertEquals(normalizedBrightness,
- mController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- /* brightnessEvent= */ null), EPSILON);
- }
-
- @Test
public void testAutoBrightnessInDoze() throws Exception {
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
@@ -1089,9 +1112,6 @@
assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
mController.getAutomaticScreenBrightness(
/* brightnessEvent= */ null), EPSILON);
- assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
- mController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- /* brightnessEvent= */ null), EPSILON);
}
@Test
@@ -1113,7 +1133,7 @@
when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
// Switch mode to DOZE
- mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
// Set policy to DOZE
mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1127,9 +1147,6 @@
// The brightness should not be scaled by the doze factor
assertEquals(normalizedBrightness,
mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
- assertEquals(normalizedBrightness,
- mController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- /* brightnessEvent= */ null), EPSILON);
}
@Test
@@ -1162,8 +1179,63 @@
// The brightness should not be scaled by the doze factor
assertEquals(normalizedBrightness,
mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
+ }
+
+ @Test
+ public void testSwitchMode_UpdateBrightnessImmediately() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // Switch mode to DOZE
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
+
assertEquals(normalizedBrightness,
- mController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- /* brightnessEvent= */ null), EPSILON);
+ mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
+ }
+
+ @Test
+ public void testSwitchMode_UpdateBrightnessInBackground() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return 0.3f as a brightness value
+ float lux = 100.0f;
+ // Brightness as float (from 0.0f to 1.0f)
+ float normalizedBrightness = 0.3f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+ when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+ /* category= */ anyInt())).thenReturn(normalizedBrightness);
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+ // Send a new sensor value
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+ // Switch mode to DOZE
+ mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ true);
+ mClock.fastForward(SystemClock.uptimeMillis());
+ mTestLooper.dispatchAll();
+
+ assertEquals(normalizedBrightness,
+ mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 8844e6c..d0eb83a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2513,9 +2513,8 @@
LogicalDisplay display =
logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
assertThat(display.isEnabledLocked()).isFalse();
- // TODO(b/332711269) make sure only one DISPLAY_GROUP_EVENT_ADDED sent.
assertThat(callback.receivedEvents()).containsExactly(DISPLAY_GROUP_EVENT_ADDED,
- DISPLAY_GROUP_EVENT_ADDED, EVENT_DISPLAY_CONNECTED).inOrder();
+ EVENT_DISPLAY_CONNECTED).inOrder();
}
@Test
@@ -3359,8 +3358,11 @@
}
displayDeviceInfo.address = new TestUtils.TestDisplayAddress();
displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
- displayManager.getDisplayDeviceRepository()
- .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+
+ displayManager.getDisplayHandler().runWithScissors(() -> {
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
+ }, 0 /* now */);
return displayDevice;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index e5685c7..98f572d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1021,6 +1021,36 @@
}
@Test
+ public void testAutoBrightnessEnabled_DisplayIsInDoze_OffloadAllows() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true);
+ when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.automaticBrightnessController).configure(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
+ /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
+ Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ );
+ verify(mHolder.hbmController)
+ .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
+ }
+
+ @Test
public void testAutoBrightnessDisabled_ManualBrightnessMode() {
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
@@ -1067,7 +1097,7 @@
}
@Test
- public void testAutoBrightnessDisabled_DisplayIsInDoze() {
+ public void testAutoBrightnessDisabled_DisplayIsInDoze_ConfigDoesNotAllow() {
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
@@ -1093,6 +1123,36 @@
}
@Test
+ public void testAutoBrightnessDisabled_DisplayIsInDoze_OffloadDoesNotAllow() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true);
+ when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.automaticBrightnessController).configure(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
+ /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
+ /* userChangedBrightness= */ false, /* adjustment= */ 0,
+ /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
+ Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+ );
+ verify(mHolder.hbmController).setAutoBrightnessEnabled(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
+ }
+
+ @Test
public void testAutoBrightnessDisabled_FollowerDisplay() {
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
@@ -1191,7 +1251,8 @@
/* ambientLightHorizonLong= */ anyInt(),
eq(lux),
eq(nits),
- any(BrightnessClamperController.class)
+ any(BrightnessClamperController.class),
+ any(DisplayManagerFlags.class)
);
}
@@ -1668,7 +1729,8 @@
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
- verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ verify(mHolder.automaticBrightnessController)
+ .switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
// Back to default mode
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
@@ -1676,7 +1738,8 @@
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
- verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT);
+ verify(mHolder.automaticBrightnessController)
+ .switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ false);
}
@Test
@@ -1690,7 +1753,7 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.automaticBrightnessController, never())
- .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ .switchMode(eq(AUTO_BRIGHTNESS_MODE_DOZE), /* sendUpdate= */ anyBoolean());
}
@Test
@@ -1703,7 +1766,7 @@
advanceTime(1); // Run updatePowerState
verify(mHolder.automaticBrightnessController, never())
- .switchMode(AUTO_BRIGHTNESS_MODE_DOZE);
+ .switchMode(eq(AUTO_BRIGHTNESS_MODE_DOZE), /* sendUpdate= */ anyBoolean());
}
@Test
@@ -1764,37 +1827,7 @@
}
@Test
- public void testInitialDozeBrightness_AutoBrightnessEnabled() {
- when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
- when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
- mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
- float brightness = 0.277f;
- when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
- when(mHolder.automaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(any(BrightnessEvent.class)))
- .thenReturn(brightness);
- when(mHolder.hbmController.getCurrentBrightnessMax())
- .thenReturn(PowerManager.BRIGHTNESS_MAX);
- when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
-
- DisplayPowerRequest dpr = new DisplayPowerRequest();
- dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
- advanceTime(1); // Run updatePowerState
-
- verify(mHolder.animator).animateTo(eq(brightness),
- /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
- /* ignoreAnimationLimits= */ anyBoolean());
- verify(mHolder.brightnessSetting).setBrightness(brightness);
- }
-
- @Test
- public void testInitialDozeBrightness_AutoBrightnessDisabled() {
+ public void testDozeManualBrightness() {
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
Settings.System.putInt(mContext.getContentResolver(),
@@ -1827,22 +1860,17 @@
}
@Test
- public void testInitialDozeBrightness_AbcIsNull() {
- when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+ public void testDozeManualBrightness_AbcIsNull() {
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
/* isAutoBrightnessAvailable= */ false);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
float brightness = 0.277f;
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
- when(mHolder.automaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(any(BrightnessEvent.class)))
- .thenReturn(brightness);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
when(mHolder.hbmController.getCurrentBrightnessMax())
.thenReturn(PowerManager.BRIGHTNESS_MAX);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
@@ -1850,13 +1878,20 @@
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
- advanceTime(1); // Run updatePowerState
+ advanceTime(1); // Run updatePowerState, initialize
- // Automatic Brightness Controller is null so no initial doze brightness should be set and
- // we should not crash
- verify(mHolder.animator, never()).animateTo(eq(brightness),
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+
+ verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR),
/* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
/* ignoreAnimationLimits= */ anyBoolean());
+ assertEquals(brightness * DOZE_SCALE_FACTOR, mHolder.dpc.getDozeBrightnessForOffload(),
+ /* delta= */ 0);
}
@Test
@@ -1864,6 +1899,8 @@
float brightness = 0.121f;
when(mPowerManagerMock.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)).thenReturn(brightness);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
@@ -1881,6 +1918,31 @@
eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
+ @Test
+ public void testDefaultDozeBrightness_ShouldNotBeUsedIfAutoBrightnessAllowedInDoze() {
+ float brightness = 0.121f;
+ when(mPowerManagerMock.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)).thenReturn(brightness);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator, never()).animateTo(eq(brightness),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -2186,7 +2248,8 @@
BrightnessRangeController brightnessRangeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController) {
+ BrightnessClamperController brightnessClamperController,
+ DisplayManagerFlags displayManagerFlags) {
return mAutomaticBrightnessController;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
index ea08be4..82acaf8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -317,7 +317,7 @@
mDisplayEventCaptor.capture());
assertThat(mLogicalDisplayCaptor.getValue()).isEqualTo(mMockedLogicalDisplay);
assertThat(mDisplayEventCaptor.getValue()).isEqualTo(EVENT_DISPLAY_CONNECTED);
- verify(mMockedLogicalDisplayMapper).setDisplayEnabledLocked(eq(mMockedLogicalDisplay),
+ verify(mMockedLogicalDisplayMapper).setEnabledLocked(eq(mMockedLogicalDisplay),
eq(false));
clearInvocations(mMockedLogicalDisplayMapper);
clearInvocations(mMockedLogicalDisplay);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 7fd96c5..12050e1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1246,6 +1246,11 @@
@Override
public void onBlockingScreenOn(Runnable unblocker) {}
+
+ @Override
+ public boolean allowAutoBrightnessInDoze() {
+ return true;
+ }
});
mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
index 8b45145..18dfcc1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
@@ -30,13 +30,21 @@
public final class TestUtils {
public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
+ return createSensorEvent(sensor, value, SystemClock.elapsedRealtimeNanos());
+ }
+
+ /**
+ * Creates a light sensor event
+ */
+ public static SensorEvent createSensorEvent(Sensor sensor, int value, long timestamp)
+ throws Exception {
final Constructor<SensorEvent> constructor =
SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
final SensorEvent event = constructor.newInstance(1);
event.sensor = sensor;
event.values[0] = value;
- event.timestamp = SystemClock.elapsedRealtimeNanos();
+ event.timestamp = timestamp;
return event;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
index 09f5bb6..498bffd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
@@ -343,17 +343,11 @@
AutomaticBrightnessController.class);
when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class)))
.thenReturn(automaticScreenBrightness);
- when(automaticBrightnessController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- any(BrightnessEvent.class)))
- .thenReturn(automaticScreenBrightness);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(automaticScreenBrightness,
mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
new BrightnessEvent(DISPLAY_ID)), 0.0f);
- assertEquals(automaticScreenBrightness,
- mAutomaticBrightnessStrategy.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- new BrightnessEvent(DISPLAY_ID)), 0.0f);
}
@Test
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 d19f479..afb5a5c 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
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -273,7 +274,8 @@
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
userSetBrightnessChanged);
- verify(mAutomaticBrightnessController, never()).switchMode(anyInt());
+ verify(mAutomaticBrightnessController, never())
+ .switchMode(anyInt(), /* sendUpdate= */ anyBoolean());
// Validate interaction when automaticBrightnessController is in non-idle mode, and display
// state is ON
@@ -282,7 +284,8 @@
allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
userSetBrightnessChanged);
verify(mAutomaticBrightnessController).switchMode(
- AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT);
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+ /* sendUpdate= */ false);
// Validate interaction when automaticBrightnessController is in non-idle mode, and display
// state is DOZE
@@ -290,7 +293,8 @@
allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
userSetBrightnessChanged);
verify(mAutomaticBrightnessController).switchMode(
- AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE);
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
+ /* sendUpdate= */ false);
}
@Test
@@ -388,17 +392,11 @@
AutomaticBrightnessController.class);
when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class)))
.thenReturn(automaticScreenBrightness);
- when(automaticBrightnessController.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- any(BrightnessEvent.class)))
- .thenReturn(automaticScreenBrightness);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(automaticScreenBrightness,
mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
new BrightnessEvent(DISPLAY_ID), false), 0.0f);
- assertEquals(automaticScreenBrightness,
- mAutomaticBrightnessStrategy.getAutomaticScreenBrightnessBasedOnLastUsedLux(
- new BrightnessEvent(DISPLAY_ID)), 0.0f);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 3399565..396f4da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -25,6 +25,7 @@
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE;
import static com.google.common.truth.Truth.assertThat;
@@ -42,17 +43,23 @@
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
+import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.testing.TestableContext;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.mockingservicestests.R;
import com.android.server.backup.FileMetadata;
+import com.android.server.backup.Flags;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.restore.PerformAdbRestoreTask;
import com.android.server.backup.restore.RestorePolicy;
@@ -61,6 +68,7 @@
import com.google.common.hash.Hashing;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -86,6 +94,8 @@
@Mock private BytesReadListener mBytesReadListenerMock;
@Mock private IBackupManagerMonitor mBackupManagerMonitorMock;
@Mock private PackageManagerInternal mMockPackageManagerInternal;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
private Context mContext;
@@ -95,7 +105,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ mContext = new TestableContext(ApplicationProvider.getApplicationContext());
mUserId = UserHandle.USER_SYSTEM;
}
@@ -515,6 +525,107 @@
@Test
public void
+ chooseRestorePolicy_flagOnNotRestoreAnyVersionVToURestoreAndInAllowlist_returnsIgnore()
+ throws Exception {
+
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ TarBackupReader tarBackupReader = createTarBackupReader();
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST, "test");
+
+ Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
+ FileMetadata info = new FileMetadata();
+ info.version = Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1;
+
+ PackageInfo packageInfo = createNonRestoreAnyVersionUPackage();
+ PackageManagerStub.sPackageInfo = packageInfo;
+
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
+ RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId, mContext);
+
+ assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mBackupManagerMonitorMock).onEvent(bundleCaptor.capture());
+ assertThat(bundleCaptor.getValue().get(EXTRA_LOG_EVENT_ID)).isEqualTo(
+ LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE);
+ }
+
+
+ @Test
+ public void
+ chooseRestorePolicy_flagOffNotRestoreAnyVersionVToURestoreAndInAllowlist_returnsAccept()
+ throws Exception {
+
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ TarBackupReader tarBackupReader = createTarBackupReader();
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST, "test");
+
+ Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
+ FileMetadata info = new FileMetadata();
+ info.version = Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1;
+
+ PackageInfo packageInfo = createNonRestoreAnyVersionUPackage();
+ PackageManagerStub.sPackageInfo = packageInfo;
+
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
+ RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId, mContext);
+
+ assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mBackupManagerMonitorMock).onEvent(bundleCaptor.capture());
+ assertThat(bundleCaptor.getValue().get(EXTRA_LOG_EVENT_ID)).isEqualTo(
+ LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER);
+
+ }
+
+ @Test
+ public void
+ chooseRestorePolicy_flagOnNotRestoreAnyVersionVToURestoreAndNotInAllowlist_returnsIgnore()
+ throws Exception {
+
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ TarBackupReader tarBackupReader = createTarBackupReader();
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST, "pkg");
+
+ Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
+ FileMetadata info = new FileMetadata();
+ info.version = Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1;
+
+ PackageInfo packageInfo = createNonRestoreAnyVersionUPackage();
+ PackageManagerStub.sPackageInfo = packageInfo;
+
+ doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
+ packageInfo.packageName);
+ RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
+ false /* allowApks */, info, signatures, mMockPackageManagerInternal,
+ mUserId, mContext);
+
+ assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mBackupManagerMonitorMock).onEvent(bundleCaptor.capture());
+ assertThat(bundleCaptor.getValue().get(EXTRA_LOG_EVENT_ID)).isEqualTo(
+ LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER);
+ }
+
+ @Test
+ public void
chooseRestorePolicy_notRestoreAnyVersionAndVersionMismatchButAllowApksAndHasApk_returnsAcceptIfApk()
throws Exception {
InputStream inputStream = mContext.getResources().openRawResource(
@@ -523,6 +634,10 @@
inputStream, null);
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
mBytesReadListenerMock, mBackupManagerMonitorMock);
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST, "pkg");
+
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
FileMetadata info = new FileMetadata();
info.version = 2;
@@ -564,6 +679,10 @@
inputStream, null);
TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
mBytesReadListenerMock, mBackupManagerMonitorMock);
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST, "pkg");
+
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
FileMetadata info = new FileMetadata();
info.version = 2;
@@ -596,5 +715,33 @@
assertThat(bundleCaptor.getValue().get(EXTRA_LOG_EVENT_ID)).isEqualTo(
LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER);
}
+
+ private TarBackupReader createTarBackupReader() throws Exception {
+ InputStream inputStream = mContext.getResources().openRawResource(
+ R.raw.backup_telephony_no_password);
+ InputStream tarInputStream = PerformAdbRestoreTask.parseBackupFileHeaderAndReturnTarStream(
+ inputStream, null);
+ TarBackupReader tarBackupReader = new TarBackupReader(tarInputStream,
+ mBytesReadListenerMock, mBackupManagerMonitorMock);
+ return tarBackupReader;
+ }
+
+ private PackageInfo createNonRestoreAnyVersionUPackage(){
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = "test";
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+ packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+ packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
+ packageInfo.applicationInfo.backupAgentName = null;
+ packageInfo.signingInfo = new SigningInfo(
+ new SigningDetails(
+ new Signature[]{FAKE_SIGNATURE_1},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null));
+ packageInfo.versionCode = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+ return packageInfo;
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/GnssNativeTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/GnssNativeTest.java
new file mode 100644
index 0000000..a14bcaf
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/GnssNativeTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.location.gnss.hal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.location.gnss.GnssConfiguration;
+import com.android.server.location.gnss.GnssPowerStats;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssNativeTest {
+
+ private @Mock Context mContext;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private FakeGnssHal mFakeGnssHal;
+ private GnssNative mGnssNative;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(mFakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(GnssNative.create(injector, mMockConfiguration)));
+ mGnssNative.register();
+ }
+
+ @Test
+ public void testRequestPowerStats_onNull_executesCallbackWithNull() {
+ mFakeGnssHal.setPowerStats(null);
+ Executor executor = spy(Runnable::run);
+ GnssNative.PowerStatsCallback callback = spy(stats -> {});
+
+ mGnssNative.requestPowerStats(executor, callback);
+
+ verify(executor).execute(any());
+ verify(callback).onReportPowerStats(null);
+ }
+
+ @Test
+ public void testRequestPowerStats_onPowerStats_executesCallbackWithStats() {
+ GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});
+ mFakeGnssHal.setPowerStats(powerStats);
+ Executor executor = spy(Runnable::run);
+ GnssNative.PowerStatsCallback callback = spy(stats -> {});
+
+ mGnssNative.requestPowerStats(executor, callback);
+
+ verify(executor).execute(any());
+ verify(callback).onReportPowerStats(powerStats);
+ }
+
+ @Test
+ public void testRequestPowerStatsBlocking_onNull_returnsNull() {
+ mFakeGnssHal.setPowerStats(null);
+
+ assertThat(mGnssNative.requestPowerStatsBlocking()).isNull();
+ }
+
+ @Test
+ public void testRequestPowerStatsBlocking_onPowerStats_returnsStats() {
+ GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});
+ mFakeGnssHal.setPowerStats(powerStats);
+
+ assertThat(mGnssNative.requestPowerStatsBlocking()).isEqualTo(powerStats);
+ }
+
+ @Test
+ public void testGetLastKnownPowerStats_onNull_preservesLastKnownPowerStats() {
+ GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});
+
+ mGnssNative.reportGnssPowerStats(powerStats);
+ assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats);
+
+ mGnssNative.reportGnssPowerStats(null);
+ assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats);
+ }
+
+ @Test
+ public void testGetLastKnownPowerStats_onPowerStats_updatesLastKnownPowerStats() {
+ GnssPowerStats powerStats1 = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 0});
+ GnssPowerStats powerStats2 = new GnssPowerStats(2, 3, 4, 5, 6, 7, 8, 9, new double[]{0, 9});
+
+ mGnssNative.reportGnssPowerStats(powerStats1);
+ assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats1);
+
+ mGnssNative.reportGnssPowerStats(powerStats2);
+ assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats2);
+ }
+
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
new file mode 100644
index 0000000..be1c121
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
@@ -0,0 +1,283 @@
+/*
+ * 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.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public class BinaryStatePowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int POWER_COMPONENT = BatteryConsumer.POWER_COMPONENT_AUDIO;
+ private static final int TEST_STATE_FLAG = 0x1;
+
+ private final MockClock mClock = new MockClock();
+ private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+ private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+
+ private static class TestBinaryStatePowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ TestBinaryStatePowerStatsProcessor(int powerComponentId,
+ double averagePowerMilliAmp, PowerStatsUidResolver uidResolver) {
+ super(powerComponentId, uidResolver, averagePowerMilliAmp);
+ }
+
+ @Override
+ protected int getBinaryState(BatteryStats.HistoryItem item) {
+ return (item.states & TEST_STATE_FLAG) != 0 ? STATE_ON : STATE_OFF;
+ }
+ }
+
+ @Test
+ public void powerProfileModel() {
+ TestBinaryStatePowerStatsProcessor processor = new TestBinaryStatePowerStatsProcessor(
+ POWER_COMPONENT, /* averagePowerMilliAmp */ 100, mUidResolver);
+
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+
+ processor.finish(stats, 11000);
+
+ // Total usage duration is 10000
+ // Total estimated power = 10000 * 100 = 1000000 mA-ms = 0.277777 mAh
+ // Screen-on - 25%
+ // Screen-off - 75%
+ double expectedPower = 0.277778;
+ long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(expectedPower * 0.25);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(expectedPower * 0.75);
+
+ // UID1 =
+ // 6000 * 100 = 600000 mA-ms = 0.166666 mAh
+ // split between three different states
+ double expectedPower1 = 0.166666;
+ long[] uidStats = new long[stats.getPowerStatsDescriptor().uidStatsArrayLength];
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+ // UID2 =
+ // 4000 * 100 = 400000 mA-ms = 0.111111 mAh
+ // all in the same state
+ double expectedPower2 = 0.111111;
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2);
+
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
+
+ @Test
+ public void energyConsumerModel() {
+ TestBinaryStatePowerStatsProcessor processor = new TestBinaryStatePowerStatsProcessor(
+ POWER_COMPONENT, /* averagePowerMilliAmp */ 100, mUidResolver);
+
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+ PersistableBundle extras = new PersistableBundle();
+ statsLayout.toExtras(extras);
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(POWER_COMPONENT,
+ statsLayout.getDeviceStatsArrayLength(), null, 0,
+ statsLayout.getUidStatsArrayLength(), extras);
+ PowerStats powerStats = new PowerStats(descriptor);
+ powerStats.stats = new long[descriptor.statsArrayLength];
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ // Establish a baseline
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+ statsLayout.setConsumedEnergy(powerStats.stats, 0, 2_160_000);
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+
+ mClock.realtime = 11000;
+ statsLayout.setConsumedEnergy(powerStats.stats, 0, 1_440_000);
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+
+ processor.finish(stats, 11000);
+
+ // Total estimated power = 3,600,000 uC = 1.0 mAh
+ // of which 3,000,000 is distributed:
+ // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+ // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+ // and 600,000 was fully with screen off:
+ // Screen-off - 1440000 uC = 0.4 mAh
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.25);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.35 + 0.4);
+
+ // UID1 =
+ // 2,160,000 uC = 0.6 mAh
+ // split between three different states
+ // fg screen-on: 2500/6000
+ // bg screen-off: 2500/6000
+ // fgs screen-off: 1000/6000
+ double expectedPower1 = 0.6;
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+ // UID2 =
+ // 1440000 mA-ms = 0.4 mAh
+ // all in the same state
+ double expectedPower2 = 0.4;
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2);
+
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
+
+
+ @NonNull
+ private BatteryStats.HistoryItem buildHistoryItem(int elapsedRealtime, boolean stateOn,
+ int uid) {
+ mClock.realtime = elapsedRealtime;
+ BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+ historyItem.time = mMonotonicClock.monotonicTime();
+ historyItem.states = stateOn ? TEST_STATE_FLAG : 0;
+ if (stateOn) {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_START;
+ } else {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+ }
+ historyItem.eventTag = historyItem.localEventTag;
+ historyItem.eventTag.uid = uid;
+ historyItem.eventTag.string = "test";
+ return historyItem;
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+ BinaryStatePowerStatsProcessor processor) {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(POWER_COMPONENT)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ aggregatedPowerStats.getPowerComponentStats(POWER_COMPONENT);
+ processor.start(powerComponentStats, 0);
+
+ powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ return powerComponentStats;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
index 752bc27..c88f0a9 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
@@ -200,7 +200,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
BluetoothPowerStatsLayout statsLayout =
new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
@@ -301,7 +301,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
BluetoothPowerStatsLayout statsLayout =
new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
@@ -408,7 +408,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
BluetoothPowerStatsLayout statsLayout =
new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
index 6b5da81..b6b759e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
@@ -128,7 +128,7 @@
states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
values(1500, 2000, 1000), 1.252578);
- mProcessor.finish(mStats);
+ mProcessor.finish(mStats, 10_000);
mStats.verifyPowerEstimates();
}
@@ -173,7 +173,7 @@
states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
values(1500, 2000, 1000), 0.80773);
- mProcessor.finish(mStats);
+ mProcessor.finish(mStats, 10_000);
mStats.verifyPowerEstimates();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
index 29ef3b6..137c2a6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
@@ -228,7 +228,7 @@
aggregatedStats.addPowerStats(powerStats, 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
MobileRadioPowerStatsLayout statsLayout =
new MobileRadioPowerStatsLayout(
@@ -475,7 +475,7 @@
aggregatedStats.addPowerStats(powerStats, 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
return aggregatedStats;
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
index 69d655b..548d54c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
@@ -203,11 +203,11 @@
aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000);
- mobileStatsProcessor.finish(mobileRadioStats);
+ mobileStatsProcessor.finish(mobileRadioStats, 10_000);
PowerComponentAggregatedPowerStats stats =
aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
- phoneStatsProcessor.finish(stats);
+ phoneStatsProcessor.finish(stats, 10_000);
PowerStatsLayout statsLayout =
new PowerStatsLayout(stats.getPowerStatsDescriptor());
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
index 3ceaf35..ff56691 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
@@ -234,7 +234,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
WifiPowerStatsLayout statsLayout =
new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
@@ -355,7 +355,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
WifiPowerStatsLayout statsLayout =
new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
@@ -454,7 +454,7 @@
aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
- processor.finish(aggregatedStats);
+ processor.finish(aggregatedStats, 10_000);
WifiPowerStatsLayout statsLayout =
new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c80e8eb..e72d9e7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5590,6 +5590,12 @@
mContext.binder.callingUid = managedProfileAdminUid;
addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+ // Profile has a unified challenge
+ doReturn(false).when(getServices().lockPatternUtils)
+ .isSeparateProfileChallengeEnabled(managedProfileUserId);
+ doReturn(true).when(getServices().lockPatternUtils)
+ .isProfileWithUnifiedChallenge(managedProfileUserId);
+
dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
@@ -5610,6 +5616,12 @@
mContext.binder.callingUid = managedProfileAdminUid;
addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+ // Profile has a unified challenge
+ doReturn(false).when(getServices().lockPatternUtils)
+ .isSeparateProfileChallengeEnabled(managedProfileUserId);
+ doReturn(true).when(getServices().lockPatternUtils)
+ .isProfileWithUnifiedChallenge(managedProfileUserId);
+
dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
dpm.setPasswordMinimumLength(admin1, 8);
dpm.setPasswordMinimumLetters(admin1, 1);
@@ -5870,6 +5882,8 @@
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
doReturn(separateChallenge).when(getServices().lockPatternUtils)
.isSeparateProfileChallengeEnabled(userId);
+ doReturn(!separateChallenge).when(getServices().lockPatternUtils)
+ .isProfileWithUnifiedChallenge(userId);
when(getServices().userManager.getCredentialOwnerProfile(userId))
.thenReturn(separateChallenge ? userId : UserHandle.USER_SYSTEM);
when(getServices().lockSettingsInternal.getUserPasswordMetrics(userId))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index c1ae852..6d86301 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -86,6 +86,8 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
@@ -94,8 +96,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x2000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index e669e7c..2296911 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -104,6 +104,8 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
@@ -112,8 +114,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x2000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mPlaybackDevice = mHdmiControlService.playback();
mDevicePowerStatusAction = DevicePowerStatusAction.create(mPlaybackDevice, ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 29d20a6..47cfa42 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -137,6 +137,7 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
@@ -150,7 +151,7 @@
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
- mNativeWrapper.setPhysicalAddress(0x0000);
+
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index d32b75b..eb4a628 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -146,6 +146,7 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
@@ -168,7 +169,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mNativeWrapper.setPhysicalAddress(0x0000);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index cb19029..bfe435c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -62,6 +62,7 @@
private HdmiCecController.HdmiCecCallback mCallback = null;
private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
private boolean mIsCecControlEnabled = true;
+ private boolean mGetPhysicalAddressCalled = false;
@Override
public String nativeInit() {
@@ -96,6 +97,7 @@
@Override
public int nativeGetPhysicalAddress() {
+ mGetPhysicalAddressCalled = true;
return mMyPhysicalAddress;
}
@@ -161,6 +163,10 @@
return mIsCecControlEnabled;
}
+ public boolean getPhysicalAddressCalled() {
+ return mGetPhysicalAddressCalled;
+ }
+
public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) {
mCecVersion = cecVersion;
}
@@ -200,6 +206,10 @@
mResultMessages.clear();
}
+ public void clearGetPhysicalAddressCallHistory() {
+ mGetPhysicalAddressCalled = false;
+ }
+
public void setPollAddressResponse(int logicalAddress, int response) {
mPollAddressResponse[logicalAddress] = response;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5502de8..0244164 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -472,6 +472,7 @@
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
mNativeWrapper.setPhysicalAddress(0x1100);
+ mHdmiControlService.onHotplug(0x1100, true);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
.isEqualTo(Constants.ABORT_NOT_IN_CORRECT_MODE);
@@ -482,6 +483,8 @@
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
mNativeWrapper.setPhysicalAddress(0x1000);
+ mHdmiControlService.onHotplug(0x1000, true);
+
mHdmiCecLocalDeviceAudioSystem.removeAction(ArcInitiationActionFromAvr.class);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 8df7d54..95a7f4b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -167,6 +167,7 @@
}
};
mHdmiCecLocalDevicePlayback.init();
+ mPlaybackPhysicalAddress = 0x2000;
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
@@ -176,13 +177,12 @@
.build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
+ mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.setPowerManagerInternal(mPowerManagerInternal);
- mPlaybackPhysicalAddress = 0x2000;
- mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
mTestLooper.dispatchAll();
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
@@ -416,6 +416,7 @@
int newPlaybackPhysicalAddress = 0x2100;
int switchPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
+ mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 192be2a..004c6c6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -181,6 +181,7 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
@@ -199,7 +200,6 @@
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
- mNativeWrapper.setPhysicalAddress(0x2000);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
}
@@ -237,6 +237,7 @@
@Test
public void handleGivePhysicalAddress_success() {
mNativeWrapper.setPhysicalAddress(0x0);
+ mHdmiControlService.onHotplug(0x0, true);
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_TV, 0, DEVICE_TV);
@Constants.HandleMessageResult
@@ -252,6 +253,7 @@
@Test
public void handleGivePhysicalAddress_failure() {
mNativeWrapper.setPhysicalAddress(Constants.INVALID_PHYSICAL_ADDRESS);
+ mHdmiControlService.onHotplug(Constants.INVALID_PHYSICAL_ADDRESS, true);
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildFeatureAbortCommand(
ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index b50684b..2a4b797 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -221,13 +221,13 @@
.setEarcSupported(true)
.build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mTvPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mTvPhysicalAddress = 0x0000;
mEarcBlocksArc = false;
- mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
mHdmiControlService.setEarcEnabled(HdmiControlManager.EARC_FEATURE_DISABLED);
mTestLooper.dispatchAll();
mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index 1ad9ce0..10f4308 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -660,4 +660,18 @@
assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
HdmiCecLocalDeviceTv.class);
}
+
+ @Test
+ public void portInfoInitiated_getPhysicalAddressCalled_readsFromHalOnFirstCallOnly() {
+ mNativeWrapper.clearGetPhysicalAddressCallHistory();
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mHdmiCecNetwork.initPortInfo();
+
+ assertThat(mHdmiCecNetwork.getPhysicalAddress()).isEqualTo(0x0000);
+ assertThat(mNativeWrapper.getPhysicalAddressCalled()).isTrue();
+
+ mNativeWrapper.clearGetPhysicalAddressCallHistory();
+ assertThat(mHdmiCecNetwork.getPhysicalAddress()).isEqualTo(0x0000);
+ assertThat(mNativeWrapper.getPhysicalAddressCalled()).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 9412ee0..3361e7f3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -115,12 +115,12 @@
.build();
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
+ mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(contextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.getHdmiCecNetwork().initPortInfo();
- mNativeWrapper.setPhysicalAddress(0x2000);
mTestLooper.dispatchAll();
mHdmiCecLocalDevicePlayback = mHdmiControlService.playback();
mHdmiCecPowerStatusController = new HdmiCecPowerStatusController(mHdmiControlService);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 298ff46..2f4a660 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -118,6 +118,8 @@
mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
setHdmiControlEnabled(hdmiControlEnabled);
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
@@ -127,8 +129,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x2000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 1d4a72f..974f64d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -111,12 +111,12 @@
.setArcSupported(false)
.build();
mNativeWrapper.setPortInfo(hdmiPortInfo);
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mHdmiControlService.initService();
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x0000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mTvDevice = mHdmiControlService.tv();
mNativeWrapper.clearResultMessages();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index cafe1e7..f8e465c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -128,6 +128,7 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
@@ -136,7 +137,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mNativeWrapper.setPhysicalAddress(0x0000);
mTestLooper.dispatchAll();
mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
index 864a182..67a3f2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
@@ -87,6 +87,8 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
@@ -95,8 +97,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x2000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mPlaybackDevice = mHdmiControlService.playback();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
index 06709cd..047a04c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
@@ -89,6 +89,8 @@
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
@@ -97,8 +99,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x0000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mTvDevice = mHdmiControlService.tv();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index 5163e29..1019db4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -188,6 +188,7 @@
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
@@ -205,7 +206,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(context);
mHdmiControlService.setPowerManager(mPowerManager);
- mNativeWrapper.setPhysicalAddress(0x0000);
mTestLooper.dispatchAll();
mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
mNativeWrapper.clearResultMessages();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 4dcc6a4..effea5a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -92,6 +92,8 @@
mHdmiControlService.setIoLooper(myLooper);
mNativeWrapper = new FakeNativeWrapper();
+ mPhysicalAddress = 0x0000;
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
@@ -115,8 +117,6 @@
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mPowerManager = new FakePowerManagerWrapper(mContextSpy);
mHdmiControlService.setPowerManager(mPowerManager);
- mPhysicalAddress = 0x0000;
- mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
mPhysicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e564ba6..15c9bfb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -10696,6 +10696,7 @@
}
@Test
+ @DisableFlags(android.app.Flags.FLAG_REMOVE_REMOTE_VIEWS)
public void testRemoveLargeRemoteViews() throws Exception {
int removeSize = mContext.getResources().getInteger(
com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
@@ -10758,6 +10759,46 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_REMOVE_REMOTE_VIEWS)
+ public void testRemoveRemoteViews() throws Exception {
+ Notification np = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentText("test")
+ .setCustomContentView(mock(RemoteViews.class))
+ .setCustomBigContentView(mock(RemoteViews.class))
+ .setCustomHeadsUpContentView(mock(RemoteViews.class))
+ .build();
+ Notification n = new Notification.Builder(mContext, "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentText("test")
+ .setCustomContentView(mock(RemoteViews.class))
+ .setCustomBigContentView(mock(RemoteViews.class))
+ .setCustomHeadsUpContentView(mock(RemoteViews.class))
+ .setPublicVersion(np)
+ .build();
+
+ assertNotNull(n.contentView);
+ assertNotNull(n.bigContentView);
+ assertNotNull(n.headsUpContentView);
+
+ assertTrue(np.extras.containsKey(Notification.EXTRA_CONTAINS_CUSTOM_VIEW));
+ assertNotNull(np.contentView);
+ assertNotNull(np.bigContentView);
+ assertNotNull(np.headsUpContentView);
+
+ mService.fixNotification(n, mPkg, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
+
+ assertNull(n.contentView);
+ assertNull(n.bigContentView);
+ assertNull(n.headsUpContentView);
+ assertNull(n.publicVersion.contentView);
+ assertNull(n.publicVersion.bigContentView);
+ assertNull(n.publicVersion.headsUpContentView);
+
+ verify(mUsageStats, times(1)).registerImageRemoved(mPkg);
+ }
+
+ @Test
public void testNotificationBubbles_flagAutoExpandForeground_fails_notForeground()
throws Exception {
setUpPrefsForBubbles(mPkg, mUid,
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index fd69217..2e571bb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -20,7 +20,7 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
-import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -288,12 +288,12 @@
}
@Keep
- private static Object[][] shortPressOnSettingsTestArguments() {
- // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey,
+ private static Object[][] settingsKeyTestArguments() {
+ // testName, testKeys, settingsKeyBehavior, expectedLogEvent, expectedKey,
// expectedModifierState
return new Object[][]{
{"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
- SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL,
+ SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL,
KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}};
}
@@ -303,7 +303,7 @@
mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID, DEVICE_BUS);
mPhoneWindowManager.overrideLaunchHome();
mPhoneWindowManager.overrideSearchKeyBehavior(
- PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY);
+ PhoneWindowManager.SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY);
mPhoneWindowManager.overrideEnableBugReportTrigger(true);
mPhoneWindowManager.overrideStatusBarManagerInternal();
mPhoneWindowManager.overrideStartActivity();
@@ -349,11 +349,11 @@
}
@Test
- @Parameters(method = "shortPressOnSettingsTestArguments")
- public void testShortPressOnSettings(String testName, int[] testKeys,
- int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey,
+ @Parameters(method = "settingsKeyTestArguments")
+ public void testSettingsKey(String testName, int[] testKeys,
+ int settingsKeyBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey,
int expectedModifierState) {
- mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior);
+ mPhoneWindowManager.overrideSettingsKeyBehavior(settingsKeyBehavior);
sendKeyCombination(testKeys, 0 /* duration */);
mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
expectedKey, expectedModifierState, DEVICE_BUS,
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index dd9d05a..fdb57d1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -496,8 +496,8 @@
mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior;
}
- void overrideShortPressOnSettingsBehavior(int behavior) {
- mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior;
+ void overrideSettingsKeyBehavior(int behavior) {
+ mPhoneWindowManager.mSettingsKeyBehavior = behavior;
}
void overrideCanStartDreaming(boolean canDream) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 12c1377..a4bec64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -57,7 +57,8 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class DesktopModeLaunchParamsModifierTests extends LaunchParamsModifierTestsBase {
+public class DesktopModeLaunchParamsModifierTests extends
+ LaunchParamsModifierTestsBase<DesktopModeLaunchParamsModifier> {
@Before
public void setUp() throws Exception {
mActivity = new ActivityBuilder(mAtm).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsModifierTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsModifierTestsBase.java
index 55f5df1..87671f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsModifierTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsModifierTestsBase.java
@@ -33,7 +33,7 @@
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
/** Common base class for launch param modifier unit test classes. */
-public class LaunchParamsModifierTestsBase extends WindowTestsBase{
+public class LaunchParamsModifierTestsBase<T extends LaunchParamsModifier> extends WindowTestsBase {
static final Rect DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
/* right */ 1920, /* bottom */ 1080);
@@ -42,7 +42,7 @@
ActivityRecord mActivity;
- LaunchParamsModifier mTarget;
+ T mTarget;
LaunchParams mCurrent;
LaunchParams mResult;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6b605ec..96ddfe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -108,6 +108,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.view.InsetsFrameProvider;
@@ -188,6 +189,7 @@
private void setUpApp(DisplayContent display) {
mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
mActivity = mTask.getTopNonFinishingActivity();
+ doReturn(false).when(mActivity).isImmersiveMode(any());
}
private void setUpDisplaySizeWithApp(int dw, int dh) {
@@ -396,6 +398,55 @@
verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
}
+ // TODO(b/333663877): Enable test after fix
+ @Test
+ @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION})
+ public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
+ final int dw = 2100;
+ final int dh = 2000;
+ final int cutoutHeight = 150;
+ final TestDisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+ .setCanRotate(false)
+ .setNotch(cutoutHeight)
+ .build();
+ setUpApp(display);
+ display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ doReturn(true).when(mActivity).isImmersiveMode(any());
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_LANDSCAPE);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final Function<ActivityRecord, Rect> innerBoundsOf =
+ (ActivityRecord a) -> {
+ final Rect bounds = new Rect();
+ a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ return bounds;
+ };
+
+ final Consumer<Integer> doubleClick =
+ (Integer y) -> {
+ mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ final Rect bounds = mActivity.getBounds();
+ assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh);
+ assertEquals(dw, bounds.width());
+
+ // Double click bottom.
+ doubleClick.accept(dh - 10);
+ assertEquals(dh, innerBoundsOf.apply(mActivity).bottom);
+
+ // Double click top.
+ doubleClick.accept(10);
+ doubleClick.accept(10);
+ assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top);
+ }
+
@Test
public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
@@ -4034,8 +4085,7 @@
// Prepare unresizable landscape activity
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
- final DisplayPolicy displayPolicy = mActivity.mDisplayContent.getDisplayPolicy();
- doReturn(immersive).when(displayPolicy).isImmersiveMode();
+ doReturn(immersive).when(mActivity).isImmersiveMode(any());
mActivity.mRootWindowContainer.performSurfacePlacement();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2ce21ba..3c921c6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -70,7 +70,8 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-public class TaskLaunchParamsModifierTests extends LaunchParamsModifierTestsBase {
+public class TaskLaunchParamsModifierTests extends
+ LaunchParamsModifierTestsBase<TaskLaunchParamsModifier> {
private static final Rect SMALL_DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
/* right */ 1000, /* bottom */ 500);
private static final Rect SMALL_DISPLAY_STABLE_BOUNDS = new Rect(/* left */ 100,
@@ -1898,8 +1899,7 @@
}
Rect startingBounds = new Rect(0, 0, 20, 20);
Rect adjustedBounds = new Rect(startingBounds);
- ((TaskLaunchParamsModifier) mTarget).adjustBoundsToAvoidConflict(displayBounds,
- existingTaskBounds, adjustedBounds);
+ mTarget.adjustBoundsToAvoidConflict(displayBounds, existingTaskBounds, adjustedBounds);
assertEquals(startingBounds, adjustedBounds);
}
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 379b45c..a23f211 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,6 +24,7 @@
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.traces.parsers.toFlickerComponent
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -174,6 +175,12 @@
}
}
+ @FlakyTest(bugId = 342596801)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
@Ignore("Not applicable to this CUJ.")
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt
new file mode 100644
index 0000000..bf569bc
--- /dev/null
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.rotation
+
+import android.platform.test.annotations.Presubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.assertions.FlickerTest
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test opening an app over lockscreen with rotation change using seamless rotations.
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenShowWhenLockedSeamlessAppRotationTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ val testApp = SeamlessRotationAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
+ device.wakeUp()
+ val originalRotation = device.displayRotation
+ ChangeDisplayOrientationRule.setRotation(Rotation.ROTATION_90)
+ Assume.assumeTrue("Assume that lockscreen uses fixed orientation",
+ originalRotation == device.displayRotation)
+ }
+ transitions {
+ // The activity is show-when-locked, so the requested orientation will be changed
+ // from NOSENSOR(keyguard) to UNSPECIFIED(activity). Then the fixed-user-rotation
+ // (by setRotation) will take effect to rotate the display.
+ testApp.launchViaIntent(wmHelper)
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+
+ @Presubmit
+ @Test
+ fun notContainsRotationAnimation() {
+ flicker.assertLayers {
+ // Verifies that com.android.wm.shell.transition.ScreenRotationAnimation is not used.
+ notContains(ComponentNameMatcher("", "Animation leash of screenshot rotation"))
+ }
+ }
+
+ // Ignore the assertions which are included in SeamlessAppRotationTest.
+ @Test
+ @Ignore("Uninterested")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun statusBarWindowIsAlwaysVisible() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun navBarLayerPositionAtStartAndEnd() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun navBarLayerIsVisibleAtStartAndEnd() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun navBarWindowIsVisibleAtStartAndEnd() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun navBarWindowIsAlwaysVisible() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
+
+ @Test
+ @Ignore("Uninterested")
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {}
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ // The rotation will be controlled by the setup of test.
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0),
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 3e500d9..45260bd 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -74,6 +74,7 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
+ android:showWhenLocked="true"
android:label="SeamlessActivity"
android:exported="true">
<intent-filter>
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
index 4c1fa6e..4995eeb 100644
--- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -30,7 +30,7 @@
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic.Kind
-import javax.tools.StandardLocation.CLASS_OUTPUT
+import javax.tools.StandardLocation.SOURCE_OUTPUT
import kotlin.collections.set
/**
@@ -126,7 +126,7 @@
@Throws(IOException::class)
private fun outputToFile(annotationTypeToIntDefMapping: Map<String, IntDefMapping>) {
val resource = processingEnv.filer.createResource(
- CLASS_OUTPUT, "com.android.winscope", outputName)
+ SOURCE_OUTPUT, "com.android.winscope", outputName)
val writer = resource.openWriter()
serializeTo(annotationTypeToIntDefMapping, writer)
writer.close()
diff --git a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
index c0c159c..d87b6df 100644
--- a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
+++ b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
@@ -24,7 +24,7 @@
import org.junit.Test
import java.io.StringWriter
import javax.tools.JavaFileObject
-import javax.tools.StandardLocation.CLASS_OUTPUT
+import javax.tools.StandardLocation.SOURCE_OUTPUT
/**
* Tests for [IntDefProcessor]
@@ -112,7 +112,7 @@
.compile(filesToCompile.toMutableList())
assertThat(compilation).succeeded()
- assertThat(compilation).generatedFile(CLASS_OUTPUT, "com.android.winscope",
+ assertThat(compilation).generatedFile(SOURCE_OUTPUT, "com.android.winscope",
"intDefMapping.json").contentsAsUtf8String().isEqualTo(expectedFile)
}