Loading Ravenwood.bp +2 −0 Original line number Diff line number Diff line Loading @@ -284,6 +284,8 @@ android_ravenwood_libgroup { "100-framework-minus-apex.ravenwood", "200-kxml2-android", "ravenwood-runtime-common-ravenwood", "android.test.mock.ravenwood", "ravenwood-helper-runtime", "hoststubgen-helper-runtime.ravenwood", Loading apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java +61 −5 Original line number Diff line number Diff line Loading @@ -19,13 +19,16 @@ import android.annotation.Nullable; import android.os.Bundle; import android.os.SystemClock; import android.perftests.utils.ShellHelper; import android.util.Log; import java.util.ArrayList; import java.util.concurrent.TimeoutException; // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java public class BenchmarkRunner { private static final long COOL_OFF_PERIOD_MS = 1000; private static final String TAG = BenchmarkRunner.class.getSimpleName(); private static final int TIMEOUT_IN_SECONDS = 45; private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90; private static final int NUM_ITERATIONS = 4; Loading Loading @@ -79,8 +82,7 @@ public class BenchmarkRunner { } 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; } Loading @@ -102,7 +104,7 @@ public class BenchmarkRunner { * to avoid unnecessary waiting. */ public void resumeTiming() { ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers"); waitCoolDownPeriod(); resumeTimer(); } Loading Loading @@ -162,4 +164,58 @@ public class BenchmarkRunner { } return null; } /** Waits for the CPU cores and the broadcast queue to be idle. */ public void waitCoolDownPeriod() { waitForCpuIdle(); waitForBroadcastIdle(); } private void waitForBroadcastIdle() { try { ShellHelper.runShellCommandWithTimeout( "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECONDS); } catch (TimeoutException e) { Log.e(TAG, "Ending waitForBroadcastIdle because it didn't finish in " + TIMEOUT_IN_SECONDS + " seconds", e); } } private void waitForCpuIdle() { int count = 0; int idleCpuPercentage; while (count++ < TIMEOUT_IN_SECONDS) { idleCpuPercentage = getIdleCpuPercentage(); Log.d(TAG, "Waiting for CPU idle #" + count + "=" + idleCpuPercentage + "%"); if (idleCpuPercentage > CPU_IDLE_THRESHOLD_PERCENTAGE) { return; } SystemClock.sleep(1000); } Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in " + TIMEOUT_IN_SECONDS + " seconds"); } 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 apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java +10 −91 Original line number Diff line number Diff line Loading @@ -188,21 +188,6 @@ public class UserLifecycleTests { } } /** 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 { Loading Loading @@ -239,7 +224,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading @@ -254,7 +238,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int userId = createUserNoFlags(); waitForBroadcastIdle(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -309,9 +292,6 @@ public class UserLifecycleTests { preStartUser(userId, numberOfIterationsToSkip); waitForBroadcastIdle(); waitCoolDownPeriod(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -353,9 +333,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitForBroadcastIdle(); waitCoolDownPeriod(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -420,7 +397,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -454,7 +430,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading @@ -466,6 +441,7 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int startUser = mAm.getCurrentUser(); final int userId = createUserNoFlags(); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading @@ -479,27 +455,6 @@ public class UserLifecycleTests { } } /** 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 { Loading @@ -507,6 +462,7 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int startUser = mAm.getCurrentUser(); final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -536,7 +492,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -562,7 +517,6 @@ public class UserLifecycleTests { /* useStaticWallpaper */true); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading Loading @@ -606,7 +560,6 @@ public class UserLifecycleTests { final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -614,7 +567,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitForBroadcastIdle(); switchUserNoCheck(startUser); mRunner.resumeTimingForNextIteration(); } Loading @@ -631,7 +583,6 @@ public class UserLifecycleTests { /* useStaticWallpaper */ true); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -639,7 +590,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitForBroadcastIdle(); switchUserNoCheck(startUser); mRunner.resumeTimingForNextIteration(); } Loading Loading @@ -675,13 +625,11 @@ public class UserLifecycleTests { @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(); Loading @@ -703,7 +651,7 @@ public class UserLifecycleTests { final int startUser = mAm.getCurrentUser(); final int userId = createUserNoFlags(); waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading @@ -726,7 +674,7 @@ public class UserLifecycleTests { final int startUser = ActivityManager.getCurrentUser(); final int userId = createUserNoFlags(); waitCoolDownPeriod(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { mRunner.resumeTiming(); Log.d(TAG, "Starting timer"); Loading @@ -752,7 +700,7 @@ public class UserLifecycleTests { switchUser(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Loading Loading @@ -781,7 +729,7 @@ public class UserLifecycleTests { switchUser(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitCoolDownPeriod(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Loading Loading @@ -827,7 +775,6 @@ public class UserLifecycleTests { Log.d(TAG, "Stopping timer"); attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId)); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -868,7 +815,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -913,7 +859,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } removeUser(userId); Loading Loading @@ -965,7 +910,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1030,7 +974,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1071,7 +1014,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1124,7 +1066,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1164,7 +1105,6 @@ public class UserLifecycleTests { runThenWaitForBroadcasts(userId, () -> { startUserInBackgroundAndWaitForUnlock(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitCoolDownPeriod(); mRunner.resumeTiming(); Log.d(TAG, "Starting timer"); Loading Loading @@ -1280,6 +1220,7 @@ public class UserLifecycleTests { * 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); Loading @@ -1296,7 +1237,7 @@ public class UserLifecycleTests { */ private void stopUserAfterWaitingForBroadcastIdle(int userId) throws RemoteException { waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); stopUser(userId); } Loading Loading @@ -1438,6 +1379,8 @@ public class UserLifecycleTests { */ private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable, String... actions) { mRunner.waitCoolDownPeriod(); final String unreceivedAction = mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions); Loading Loading @@ -1538,28 +1481,4 @@ public class UserLifecycleTests { 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); } } core/java/android/app/ActivityManager.java +71 −38 Original line number Diff line number Diff line Loading @@ -1400,18 +1400,10 @@ public class ActivityManager { */ public static final int RESTRICTION_SUBREASON_MAX_LENGTH = 16; /** * Restriction reason unknown - do not use directly. * * For use with noteAppRestrictionEnabled() * @hide */ public static final int RESTRICTION_REASON_UNKNOWN = 0; /** * Restriction reason to be used when this is normal behavior for the state. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_DEFAULT = 1; Loading @@ -1420,7 +1412,7 @@ public class ActivityManager { * Restriction reason is some kind of timeout that moves the app to a more restricted state. * The threshold should specify how long the app was dormant, in milliseconds. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_DORMANT = 2; Loading @@ -1429,7 +1421,7 @@ public class ActivityManager { * Restriction reason to be used when removing a restriction due to direct or indirect usage * of the app, especially to undo any automatic restrictions. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_USAGE = 3; Loading @@ -1438,63 +1430,102 @@ public class ActivityManager { * Restriction reason to be used when the user chooses to manually restrict the app, through * UI or command line interface. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_USER = 4; /** * Restriction reason to be used when the user chooses to manually restrict the app on being * prompted by the OS or some anomaly detection algorithm. For example, if the app is causing * high battery drain or affecting system performance and the OS recommends that the user * restrict the app. * * For use with noteAppRestrictionEnabled() * @hide */ public static final int RESTRICTION_REASON_USER_NUDGED = 5; /** * Restriction reason to be used when the OS automatically detects that the app is causing * system health issues such as performance degradation, battery drain, high memory usage, etc. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_SYSTEM_HEALTH = 6; public static final int RESTRICTION_REASON_SYSTEM_HEALTH = 5; /** * Restriction reason to be used when there is a server-side decision made to restrict an app * that is showing widespread problems on user devices, or violating policy in some way. * Restriction reason to be used when app is doing something that is against policy, such as * spamming the user or being deceptive about its intentions. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_REMOTE_TRIGGER = 7; public static final int RESTRICTION_REASON_POLICY = 6; /** * Restriction reason to be used when some other problem requires restricting the app. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_OTHER = 8; public static final int RESTRICTION_REASON_OTHER = 7; /** @hide */ @IntDef(prefix = { "RESTRICTION_REASON_" }, value = { RESTRICTION_REASON_UNKNOWN, RESTRICTION_REASON_DEFAULT, RESTRICTION_REASON_DORMANT, RESTRICTION_REASON_USAGE, RESTRICTION_REASON_USER, RESTRICTION_REASON_USER_NUDGED, RESTRICTION_REASON_SYSTEM_HEALTH, RESTRICTION_REASON_REMOTE_TRIGGER, RESTRICTION_REASON_POLICY, RESTRICTION_REASON_OTHER }) @Retention(RetentionPolicy.SOURCE) public @interface RestrictionReason{} /** * The source of restriction is the user manually choosing to do so. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_USER = 1; /** * The source of restriction is the user, on being prompted by the system for the specified * reason. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_USER_NUDGED = 2; /** * The source of restriction is the system. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_SYSTEM = 3; /** * The source of restriction is the command line interface through the shell or a test. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_COMMAND_LINE = 4; /** * The source of restriction is a configuration pushed from a server. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_REMOTE_TRIGGER = 5; /** @hide */ @IntDef(prefix = { "RESTRICTION_SOURCE_" }, value = { RESTRICTION_SOURCE_USER, RESTRICTION_SOURCE_USER_NUDGED, RESTRICTION_SOURCE_SYSTEM, RESTRICTION_SOURCE_COMMAND_LINE, RESTRICTION_SOURCE_REMOTE_TRIGGER, }) @Retention(RetentionPolicy.SOURCE) public @interface RestrictionSource{} /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String restrictionLevelToName(@RestrictionLevel int level) { Loading Loading @@ -6254,7 +6285,7 @@ public class ActivityManager { * <p> * The {@code enabled} value determines whether the state is being applied or removed. * Not all restrictions are actual restrictions. For example, * {@link #RESTRICTION_LEVEL_ADAPTIVE} is a normal state, where there is default lifecycle * {@link #RESTRICTION_LEVEL_ADAPTIVE_BUCKET} is a normal state, where there is default lifecycle * management applied to the app. Also, {@link #RESTRICTION_LEVEL_EXEMPTED} is used when the * app is being put in a power-save allowlist. * <p> Loading @@ -6267,6 +6298,7 @@ public class ActivityManager { * true, * RESTRICTION_REASON_USER, * "settings", * RESTRICTION_SOURCE_USER, * 0); * </pre> * Example arguments when app is put in restricted standby bucket for exceeding X hours of jobs: Loading @@ -6278,6 +6310,7 @@ public class ActivityManager { * true, * RESTRICTION_REASON_SYSTEM_HEALTH, * "job_duration", * RESTRICTION_SOURCE_SYSTEM, * X * 3600 * 1000L); * </pre> * Loading @@ -6295,7 +6328,7 @@ public class ActivityManager { * Examples of system resource usage: wakelock, wakeups, mobile_data, * binder_calls, memory, excessive_threads, excessive_cpu, gps_scans, etc. * Examples of user actions: settings, notification, command_line, launch, etc. * * @param source the source of the action, from {@code RestrictionSource} * @param threshold for reasons that are due to exceeding some threshold, the threshold value * must be specified. The unit of the threshold depends on the reason and/or * subReason. For time, use milliseconds. For memory, use KB. For count, use Loading @@ -6308,10 +6341,10 @@ public class ActivityManager { public void noteAppRestrictionEnabled(@NonNull String packageName, int uid, @RestrictionLevel int restrictionLevel, boolean enabled, @RestrictionReason int reason, @Nullable String subReason, long threshold) { @Nullable String subReason, @RestrictionSource int source, long threshold) { try { getService().noteAppRestrictionEnabled(packageName, uid, restrictionLevel, enabled, reason, subReason, threshold); reason, subReason, source, threshold); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading core/java/android/app/IActivityManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -1016,5 +1016,5 @@ interface IActivityManager { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DEVICE_POWER)") void noteAppRestrictionEnabled(in String packageName, int uid, int restrictionType, boolean enabled, int reason, in String subReason, long threshold); boolean enabled, int reason, in String subReason, int source, long threshold); } Loading
Ravenwood.bp +2 −0 Original line number Diff line number Diff line Loading @@ -284,6 +284,8 @@ android_ravenwood_libgroup { "100-framework-minus-apex.ravenwood", "200-kxml2-android", "ravenwood-runtime-common-ravenwood", "android.test.mock.ravenwood", "ravenwood-helper-runtime", "hoststubgen-helper-runtime.ravenwood", Loading
apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java +61 −5 Original line number Diff line number Diff line Loading @@ -19,13 +19,16 @@ import android.annotation.Nullable; import android.os.Bundle; import android.os.SystemClock; import android.perftests.utils.ShellHelper; import android.util.Log; import java.util.ArrayList; import java.util.concurrent.TimeoutException; // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java public class BenchmarkRunner { private static final long COOL_OFF_PERIOD_MS = 1000; private static final String TAG = BenchmarkRunner.class.getSimpleName(); private static final int TIMEOUT_IN_SECONDS = 45; private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90; private static final int NUM_ITERATIONS = 4; Loading Loading @@ -79,8 +82,7 @@ public class BenchmarkRunner { } 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; } Loading @@ -102,7 +104,7 @@ public class BenchmarkRunner { * to avoid unnecessary waiting. */ public void resumeTiming() { ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers"); waitCoolDownPeriod(); resumeTimer(); } Loading Loading @@ -162,4 +164,58 @@ public class BenchmarkRunner { } return null; } /** Waits for the CPU cores and the broadcast queue to be idle. */ public void waitCoolDownPeriod() { waitForCpuIdle(); waitForBroadcastIdle(); } private void waitForBroadcastIdle() { try { ShellHelper.runShellCommandWithTimeout( "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECONDS); } catch (TimeoutException e) { Log.e(TAG, "Ending waitForBroadcastIdle because it didn't finish in " + TIMEOUT_IN_SECONDS + " seconds", e); } } private void waitForCpuIdle() { int count = 0; int idleCpuPercentage; while (count++ < TIMEOUT_IN_SECONDS) { idleCpuPercentage = getIdleCpuPercentage(); Log.d(TAG, "Waiting for CPU idle #" + count + "=" + idleCpuPercentage + "%"); if (idleCpuPercentage > CPU_IDLE_THRESHOLD_PERCENTAGE) { return; } SystemClock.sleep(1000); } Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in " + TIMEOUT_IN_SECONDS + " seconds"); } 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
apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java +10 −91 Original line number Diff line number Diff line Loading @@ -188,21 +188,6 @@ public class UserLifecycleTests { } } /** 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 { Loading Loading @@ -239,7 +224,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading @@ -254,7 +238,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int userId = createUserNoFlags(); waitForBroadcastIdle(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -309,9 +292,6 @@ public class UserLifecycleTests { preStartUser(userId, numberOfIterationsToSkip); waitForBroadcastIdle(); waitCoolDownPeriod(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -353,9 +333,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitForBroadcastIdle(); waitCoolDownPeriod(); runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -420,7 +397,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -454,7 +430,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading @@ -466,6 +441,7 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int startUser = mAm.getCurrentUser(); final int userId = createUserNoFlags(); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading @@ -479,27 +455,6 @@ public class UserLifecycleTests { } } /** 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 { Loading @@ -507,6 +462,7 @@ public class UserLifecycleTests { mRunner.pauseTiming(); final int startUser = mAm.getCurrentUser(); final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true); mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading Loading @@ -536,7 +492,6 @@ public class UserLifecycleTests { while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -562,7 +517,6 @@ public class UserLifecycleTests { /* useStaticWallpaper */true); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading Loading @@ -606,7 +560,6 @@ public class UserLifecycleTests { final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -614,7 +567,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitForBroadcastIdle(); switchUserNoCheck(startUser); mRunner.resumeTimingForNextIteration(); } Loading @@ -631,7 +583,6 @@ public class UserLifecycleTests { /* useStaticWallpaper */ true); while (mRunner.keepRunning()) { mRunner.pauseTiming(); waitCoolDownPeriod(); Log.d(TAG, "Starting timer"); mRunner.resumeTiming(); Loading @@ -639,7 +590,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitForBroadcastIdle(); switchUserNoCheck(startUser); mRunner.resumeTimingForNextIteration(); } Loading Loading @@ -675,13 +625,11 @@ public class UserLifecycleTests { @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(); Loading @@ -703,7 +651,7 @@ public class UserLifecycleTests { final int startUser = mAm.getCurrentUser(); final int userId = createUserNoFlags(); waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { mRunner.resumeTiming(); Log.i(TAG, "Starting timer"); Loading @@ -726,7 +674,7 @@ public class UserLifecycleTests { final int startUser = ActivityManager.getCurrentUser(); final int userId = createUserNoFlags(); waitCoolDownPeriod(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> { mRunner.resumeTiming(); Log.d(TAG, "Starting timer"); Loading @@ -752,7 +700,7 @@ public class UserLifecycleTests { switchUser(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Loading Loading @@ -781,7 +729,7 @@ public class UserLifecycleTests { switchUser(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitCoolDownPeriod(); mRunner.waitCoolDownPeriod(); mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> { runThenWaitForBroadcasts(userId, () -> { mRunner.resumeTiming(); Loading Loading @@ -827,7 +775,6 @@ public class UserLifecycleTests { Log.d(TAG, "Stopping timer"); attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId)); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -868,7 +815,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -913,7 +859,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } removeUser(userId); Loading Loading @@ -965,7 +910,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1030,7 +974,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1071,7 +1014,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1124,7 +1066,6 @@ public class UserLifecycleTests { mRunner.pauseTiming(); Log.d(TAG, "Stopping timer"); removeUser(userId); waitCoolDownPeriod(); mRunner.resumeTimingForNextIteration(); } } Loading Loading @@ -1164,7 +1105,6 @@ public class UserLifecycleTests { runThenWaitForBroadcasts(userId, () -> { startUserInBackgroundAndWaitForUnlock(userId); }, Intent.ACTION_MEDIA_MOUNTED); waitCoolDownPeriod(); mRunner.resumeTiming(); Log.d(TAG, "Starting timer"); Loading Loading @@ -1280,6 +1220,7 @@ public class UserLifecycleTests { * 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); Loading @@ -1296,7 +1237,7 @@ public class UserLifecycleTests { */ private void stopUserAfterWaitingForBroadcastIdle(int userId) throws RemoteException { waitForBroadcastIdle(); mRunner.waitCoolDownPeriod(); stopUser(userId); } Loading Loading @@ -1438,6 +1379,8 @@ public class UserLifecycleTests { */ private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable, String... actions) { mRunner.waitCoolDownPeriod(); final String unreceivedAction = mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions); Loading Loading @@ -1538,28 +1481,4 @@ public class UserLifecycleTests { 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); } }
core/java/android/app/ActivityManager.java +71 −38 Original line number Diff line number Diff line Loading @@ -1400,18 +1400,10 @@ public class ActivityManager { */ public static final int RESTRICTION_SUBREASON_MAX_LENGTH = 16; /** * Restriction reason unknown - do not use directly. * * For use with noteAppRestrictionEnabled() * @hide */ public static final int RESTRICTION_REASON_UNKNOWN = 0; /** * Restriction reason to be used when this is normal behavior for the state. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_DEFAULT = 1; Loading @@ -1420,7 +1412,7 @@ public class ActivityManager { * Restriction reason is some kind of timeout that moves the app to a more restricted state. * The threshold should specify how long the app was dormant, in milliseconds. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_DORMANT = 2; Loading @@ -1429,7 +1421,7 @@ public class ActivityManager { * Restriction reason to be used when removing a restriction due to direct or indirect usage * of the app, especially to undo any automatic restrictions. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_USAGE = 3; Loading @@ -1438,63 +1430,102 @@ public class ActivityManager { * Restriction reason to be used when the user chooses to manually restrict the app, through * UI or command line interface. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_USER = 4; /** * Restriction reason to be used when the user chooses to manually restrict the app on being * prompted by the OS or some anomaly detection algorithm. For example, if the app is causing * high battery drain or affecting system performance and the OS recommends that the user * restrict the app. * * For use with noteAppRestrictionEnabled() * @hide */ public static final int RESTRICTION_REASON_USER_NUDGED = 5; /** * Restriction reason to be used when the OS automatically detects that the app is causing * system health issues such as performance degradation, battery drain, high memory usage, etc. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_SYSTEM_HEALTH = 6; public static final int RESTRICTION_REASON_SYSTEM_HEALTH = 5; /** * Restriction reason to be used when there is a server-side decision made to restrict an app * that is showing widespread problems on user devices, or violating policy in some way. * Restriction reason to be used when app is doing something that is against policy, such as * spamming the user or being deceptive about its intentions. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_REMOTE_TRIGGER = 7; public static final int RESTRICTION_REASON_POLICY = 6; /** * Restriction reason to be used when some other problem requires restricting the app. * * For use with noteAppRestrictionEnabled() * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_REASON_OTHER = 8; public static final int RESTRICTION_REASON_OTHER = 7; /** @hide */ @IntDef(prefix = { "RESTRICTION_REASON_" }, value = { RESTRICTION_REASON_UNKNOWN, RESTRICTION_REASON_DEFAULT, RESTRICTION_REASON_DORMANT, RESTRICTION_REASON_USAGE, RESTRICTION_REASON_USER, RESTRICTION_REASON_USER_NUDGED, RESTRICTION_REASON_SYSTEM_HEALTH, RESTRICTION_REASON_REMOTE_TRIGGER, RESTRICTION_REASON_POLICY, RESTRICTION_REASON_OTHER }) @Retention(RetentionPolicy.SOURCE) public @interface RestrictionReason{} /** * The source of restriction is the user manually choosing to do so. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_USER = 1; /** * The source of restriction is the user, on being prompted by the system for the specified * reason. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_USER_NUDGED = 2; /** * The source of restriction is the system. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_SYSTEM = 3; /** * The source of restriction is the command line interface through the shell or a test. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_COMMAND_LINE = 4; /** * The source of restriction is a configuration pushed from a server. * * @see #noteAppRestrictionEnabled(String, int, int, boolean, int, String, int, long) * @hide */ public static final int RESTRICTION_SOURCE_REMOTE_TRIGGER = 5; /** @hide */ @IntDef(prefix = { "RESTRICTION_SOURCE_" }, value = { RESTRICTION_SOURCE_USER, RESTRICTION_SOURCE_USER_NUDGED, RESTRICTION_SOURCE_SYSTEM, RESTRICTION_SOURCE_COMMAND_LINE, RESTRICTION_SOURCE_REMOTE_TRIGGER, }) @Retention(RetentionPolicy.SOURCE) public @interface RestrictionSource{} /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static String restrictionLevelToName(@RestrictionLevel int level) { Loading Loading @@ -6254,7 +6285,7 @@ public class ActivityManager { * <p> * The {@code enabled} value determines whether the state is being applied or removed. * Not all restrictions are actual restrictions. For example, * {@link #RESTRICTION_LEVEL_ADAPTIVE} is a normal state, where there is default lifecycle * {@link #RESTRICTION_LEVEL_ADAPTIVE_BUCKET} is a normal state, where there is default lifecycle * management applied to the app. Also, {@link #RESTRICTION_LEVEL_EXEMPTED} is used when the * app is being put in a power-save allowlist. * <p> Loading @@ -6267,6 +6298,7 @@ public class ActivityManager { * true, * RESTRICTION_REASON_USER, * "settings", * RESTRICTION_SOURCE_USER, * 0); * </pre> * Example arguments when app is put in restricted standby bucket for exceeding X hours of jobs: Loading @@ -6278,6 +6310,7 @@ public class ActivityManager { * true, * RESTRICTION_REASON_SYSTEM_HEALTH, * "job_duration", * RESTRICTION_SOURCE_SYSTEM, * X * 3600 * 1000L); * </pre> * Loading @@ -6295,7 +6328,7 @@ public class ActivityManager { * Examples of system resource usage: wakelock, wakeups, mobile_data, * binder_calls, memory, excessive_threads, excessive_cpu, gps_scans, etc. * Examples of user actions: settings, notification, command_line, launch, etc. * * @param source the source of the action, from {@code RestrictionSource} * @param threshold for reasons that are due to exceeding some threshold, the threshold value * must be specified. The unit of the threshold depends on the reason and/or * subReason. For time, use milliseconds. For memory, use KB. For count, use Loading @@ -6308,10 +6341,10 @@ public class ActivityManager { public void noteAppRestrictionEnabled(@NonNull String packageName, int uid, @RestrictionLevel int restrictionLevel, boolean enabled, @RestrictionReason int reason, @Nullable String subReason, long threshold) { @Nullable String subReason, @RestrictionSource int source, long threshold) { try { getService().noteAppRestrictionEnabled(packageName, uid, restrictionLevel, enabled, reason, subReason, threshold); reason, subReason, source, threshold); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading
core/java/android/app/IActivityManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -1016,5 +1016,5 @@ interface IActivityManager { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DEVICE_POWER)") void noteAppRestrictionEnabled(in String packageName, int uid, int restrictionType, boolean enabled, int reason, in String subReason, long threshold); boolean enabled, int reason, in String subReason, int source, long threshold); }