Fixed (again :-( am start-user for profiles.

Previously it was requiring the caller to explicitly pass
--force-invisible to start a profile invisible when its parent is not
the current user, but that would break existing CTS tests.

Here's an example on how to reproduce the issue:

$ adb shell pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE DollyTheSheep
Success: created user id 12

$ adb shell pm create-user Homer
Success: created user id 13

$ adb shell am switch-user 13
$ adb shell cmd user list --all -v

3 users:

0: id=0, name=Owner, type=full.SYSTEM, flags=ADMIN|FULL|INITIALIZED|MAIN|PRIMARY|SYSTEM (running)
1: id=12, name=DollyTheSheep, type=profile.CLONE, flags=PROFILE (parentId=0)
2: id=13, name=Homer, type=full.SECONDARY, flags=FULL|INITIALIZED (running) (current) (visible)

$ adb shell am start-user 12
Error: could not start user

03-07 22:56:32.621 22936 28819 W UserVisibilityMediator: getUserVisibilityOnStartLocked(12, 0, BACKGROUND_VISIBLE, 0) failed: cannot start profile user visible when its parent is not visible in that display

$ adb shell cmd user is-user-visible 12
false

And how it's working now:

$ adb shell am get-current-user
13

$ adb shell am start-user 12
Success: user started

$ adb shell cmd user is-user-visible 12
false

$ adb shell cmd user list --all -v

3 users:

0: id=0, name=Owner, type=full.SYSTEM, flags=ADMIN|FULL|INITIALIZED|MAIN|PRIMARY|SYSTEM (running)
1: id=12, name=DollyTheSheep, type=profile.CLONE, flags=INITIALIZED|PROFILE (parentId=0) (running)
2: id=13, name=Homer, type=full.SECONDARY, flags=FULL|INITIALIZED (running) (current) (visible)

Test: see above
Fixes: 272089437

Change-Id: Ib9cc2d3bf4092bf7899a7b7e9d9bb4bf904c5a4a
This commit is contained in:
Felipe Leme
2023-03-07 14:53:17 -08:00
parent 3fd4802dad
commit ecdc6fb6a3

View File

@@ -46,6 +46,7 @@ import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -2222,7 +2223,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
boolean wait = false;
String opt;
int displayId = Display.INVALID_DISPLAY;
boolean forceInvisible = false;
while ((opt = getNextOption()) != null) {
switch(opt) {
case "-w":
@@ -2231,27 +2231,31 @@ final class ActivityManagerShellCommand extends ShellCommand {
case "--display":
displayId = getDisplayIdFromNextArg();
break;
case "--force-invisible":
forceInvisible = true;
break;
default:
getErrPrintWriter().println("Error: unknown option: " + opt);
return -1;
}
}
final int userId = Integer.parseInt(getNextArgRequired());
final boolean callStartProfile = !forceInvisible && isProfile(userId);
final ProgressWaiter waiter = wait ? new ProgressWaiter(userId) : null;
Slogf.d(TAG, "runStartUser(): userId=%d, display=%d, waiter=%s, callStartProfile=%b, "
+ "forceInvisible=%b", userId, displayId, waiter, callStartProfile,
forceInvisible);
// For backwards compatibility, if the user is a profile, we need to define whether it
// should be started visible (when its parent is the current user) or not (when it isn't)
final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
final int parentUserId = umi.getProfileParentId(userId);
final int currentUserId = ami.getCurrentUserId();
final boolean isProfile = parentUserId != userId;
final boolean isVisibleProfile = isProfile && parentUserId == currentUserId;
Slogf.d(TAG, "runStartUser(): userId=%d, parentUserId=%d, currentUserId=%d, isProfile=%b, "
+ "isVisibleProfile=%b, display=%d, waiter=%s", userId, parentUserId, currentUserId,
isProfile, isVisibleProfile, displayId, waiter);
boolean success;
String displaySuffix = "";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runStartUser" + userId);
try {
if (callStartProfile) {
if (isVisibleProfile) {
Slogf.d(TAG, "calling startProfileWithListener(%d, %s)", userId, waiter);
// startProfileWithListener() will start the profile visible (as long its parent is
// the current user), while startUserInBackgroundWithListener() will always start
@@ -3906,11 +3910,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
return new Resources(AssetManager.getSystem(), metrics, config);
}
private boolean isProfile(@UserIdInt int userId) {
final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
return umi.getProfileParentId(userId) != userId;
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -4158,7 +4157,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" execution of that user if it is currently stopped.");
pw.println(" get-current-user");
pw.println(" Returns id of the current foreground user.");
pw.println(" start-user [-w] [--display DISPLAY_ID] [--force-invisible] <USER_ID>");
pw.println(" start-user [-w] [--display DISPLAY_ID] <USER_ID>");
pw.println(" Start USER_ID in background if it is currently stopped;");
pw.println(" use switch-user if you want to start the user in foreground.");
pw.println(" -w: wait for start-user to complete and the user to be unlocked.");
@@ -4166,10 +4165,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
+ "which allows the user to launch activities on it.");
pw.println(" (not supported on all devices; typically only on automotive builds "
+ "where the vehicle has passenger displays)");
pw.println(" --force-invisible: always start the user invisible, even if it's a "
+ "profile.");
pw.println(" (by default, a profile is visible in the default display when its "
+ "parent is the current foreground user)");
pw.println(" unlock-user <USER_ID>");
pw.println(" Unlock the given user. This will only work if the user doesn't");
pw.println(" have an LSKF (PIN/pattern/password).");