New TestAPIs on ActivityManager to not stop bg users on switch.

Test: atest NeneTest:UsersTest
Test: m update-api
Test: adb shell cmd activity set-stop-user-on-switch false
Test: adb shell cmd activity set-stop-user-on-switch
Test: adb shell dumpsys activity users|grep OnSwitch

Bug: 203752848

Change-Id: Ib57989aff323dc1f7d98720d01215e4f7c79ba3a
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 488f8b1..40880c1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -113,6 +113,7 @@
     method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -127,6 +128,9 @@
     field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
     field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
     field public static final int PROCESS_STATE_TOP = 2; // 0x2
+    field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+    field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
+    field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f53c5b6..50a562b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4076,6 +4076,56 @@
     }
 
     /**
+     * Uses the value defined by the platform.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+
+    /**
+     * Overrides value defined by the platform and stop background users on switch.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+
+    /**
+     * Overrides value defined by the platform and don't stop background users on switch.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+
+    /** @hide */
+    @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
+            STOP_BG_USERS_ON_SWITCH_DEFAULT,
+            STOP_BG_USERS_ON_SWITCH_TRUE,
+            STOP_BG_USERS_ON_SWITCH_FALSE
+    })
+    public @interface StopBgUsersOnSwitch {}
+
+    /**
+     * Sets whether background users should be stopped when the current user is switched.
+     *
+     * <p>Should only be used on tests.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
+    public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        try {
+            getService().setStopBackgroundUsersOnSwitch(value);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starts a profile.
      * To be used with non-managed profiles, managed profiles should use
      * {@link UserManager#requestQuietModeEnabled}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f8c8aa3..b416c95 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -661,6 +663,11 @@
             @Nullable VoiceInteractionManagerProvider provider);
 
     /**
+     * Sets whether background users should be stopped when the current user is switched.
+     */
+    public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+
+    /**
      * Provides the interface to communicate between voice interaction manager service and
      * ActivityManagerService.
      */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b90b9a1..601ec9a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -341,6 +341,7 @@
     @UnsupportedAppUsage
     boolean switchUser(int userid);
     @UnsupportedAppUsage
+    void setStopBackgroundUsersOnSwitch(int value);
     boolean removeTask(int taskId);
     @UnsupportedAppUsage
     void registerProcessObserver(in IProcessObserver observer);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 18ed958..36dcc8d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -31,6 +31,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.AppOpsManager.OP_NONE;
@@ -15096,6 +15097,11 @@
     }
 
     @Override
+    public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        mUserController.setStopBackgroundUsersOnSwitch(value);
+    }
+
+    @Override
     public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
         return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false,
                 /* callback= */ callback, /* keyEvictedCallback= */ null);
@@ -16392,6 +16398,11 @@
                 @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
             ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
         }
+
+        @Override
+        public void setStopBackgroundUsersOnSwitch(int value) {
+            ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 685d606..60b2149 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -29,6 +29,8 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
 
 import android.app.ActivityManager;
@@ -100,6 +102,7 @@
 import com.android.internal.util.MemInfoReader;
 import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.compat.PlatformCompat;
+import com.android.server.utils.Slogf;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -128,6 +131,10 @@
 import javax.microedition.khronos.egl.EGLSurface;
 
 final class ActivityManagerShellCommand extends ShellCommand {
+
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerShellCommand" : TAG_AM;
+
+
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
 
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -323,6 +330,8 @@
                     return runServiceRestartBackoff(pw);
                 case "get-isolated-pids":
                     return runGetIsolatedProcesses(pw);
+                case "set-stop-user-on-switch":
+                    return runSetStopBackgroundUsersOnSwitch(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -3157,6 +3166,30 @@
         return 0;
     }
 
+    private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                "setStopBackgroundUsersOnSwitch()");
+        String arg = getNextArg();
+        if (arg == null) {
+            Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
+            mInternal.setStopBackgroundUsersOnSwitch(
+                    ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+            pw.println("Reset to default value");
+            return 0;
+        }
+
+        boolean stop = Boolean.parseBoolean(arg);
+        int value = stop
+                ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
+                : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+
+        Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
+        mInternal.setStopBackgroundUsersOnSwitch(value);
+        pw.println("Set to " + stop);
+
+        return 0;
+    }
+
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
@@ -3489,6 +3522,10 @@
             pw.println("            Shows the restart backoff policy state for <PACKAGE_NAME>.");
             pw.println("  get-isolated-pids <UID>");
             pw.println("         Get the PIDs of isolated processes with packages in this <UID>");
+            pw.println("  set-stop-user-on-switch [true|false]");
+            pw.println("         Sets whether the current user (and its profiles) should be stopped"
+                    + " when switching to a different user.");
+            pw.println("         Without arguments, it resets to the value defined by platform.");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba3e1fb..b8be6c5 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,6 +19,9 @@
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopBgUsersOnSwitch;
 import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -368,6 +371,13 @@
     @GuardedBy("mLock")
     private boolean mInitialized;
 
+    /**
+     * Defines the behavior of whether the background users should be stopped when the foreground
+     * user is switched.
+     */
+    @GuardedBy("mLock")
+    private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+
     UserController(ActivityManagerService service) {
         this(new Injector(service));
     }
@@ -408,8 +418,33 @@
         }
     }
 
+    void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+        if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
+                == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException(
+                    "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
+                            + "call setStopBackgroundUsersOnSwitch()");
+        }
+
+        synchronized (mLock) {
+            Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
+                    mStopBgUsersOnSwitch, value);
+            mStopBgUsersOnSwitch = value;
+        }
+    }
+
     private boolean shouldStopBackgroundUsersOnSwitch() {
-        int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
+        synchronized (mLock) {
+            if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
+                final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
+                Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
+                        value);
+                return value;
+            }
+        }
+        final int property = SystemProperties.getInt("fw.stop_bg_users_on_switch", -1);
         return property == -1 ? mDelayUserDataLocking : property == 1;
     }
 
@@ -2611,8 +2646,9 @@
             pw.println("  mTargetUserId:" + mTargetUserId);
             pw.println("  mLastActiveUsers:" + mLastActiveUsers);
             pw.println("  mDelayUserDataLocking:" + mDelayUserDataLocking);
-            pw.println("  shouldStopBackgroundUsersOnSwitch:"
+            pw.println("  shouldStopBackgroundUsersOnSwitch():"
                     + shouldStopBackgroundUsersOnSwitch());
+            pw.println("  mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
             pw.println("  mMaxRunningUsers:" + mMaxRunningUsers);
             pw.println("  mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
             pw.println("  mInitialized:" + mInitialized);