Added user visibility callbacks on SystemService.
On Android U, system components (like location) should rely on UM.isUserVisible() (rather than AM.getCurrentUser()) to check if a user is "visible". Hence, we need to provide a callback mechanism to notify when the user visibility changes. This change adds a SystemService.onUserVisibilityChanged() API, and also refactors how some internal components (like UserController and ActivityManagerService) handle the initial start of user 0. Finally, it also changes some user-related event logs to include more info. Bug: 244333150 Test: atest UserControllerTest # to check for breakage only Test: m update-api Test: adb shell dumpsys activity users|grep mVisibleUsers Test: adb logcat -D -b events | egrep '(I uc_|I ssm_)' Change-Id: I6898a7fc553b2bc600151715707b409b68e4880d
This commit is contained in:
@@ -977,6 +977,7 @@ message UserControllerProto {
|
||||
optional int32 profile = 2;
|
||||
}
|
||||
repeated UserProfile user_profile_group_ids = 4;
|
||||
repeated int32 visible_users_array = 5;
|
||||
}
|
||||
|
||||
// sync with com.android.server.am.AppTimeTracker.java
|
||||
|
||||
@@ -472,6 +472,18 @@ public abstract class SystemService {
|
||||
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link UserManager#isUserVisible() user visibility} changed.
|
||||
*
|
||||
* <p>This callback is called before the user starts or is switched to (or after it stops), when
|
||||
* its visibility changed because of that action.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// NOTE: change visible to int if this method becomes a @SystemApi
|
||||
public void onUserVisibilityChanged(@NonNull TargetUser user, boolean visible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an existing user is stopping, for system services to finalize any per-user
|
||||
* state they maintain for running users. This is called prior to sending the SHUTDOWN
|
||||
|
||||
@@ -75,13 +75,17 @@ public final class SystemServiceManager implements Dumpable {
|
||||
// Constants used on onUser(...)
|
||||
// NOTE: do not change their values, as they're used on Trace calls and changes might break
|
||||
// performance tests that rely on them.
|
||||
private static final String USER_STARTING = "Start"; // Logged as onStartUser
|
||||
private static final String USER_UNLOCKING = "Unlocking"; // Logged as onUnlockingUser
|
||||
private static final String USER_UNLOCKED = "Unlocked"; // Logged as onUnlockedUser
|
||||
private static final String USER_SWITCHING = "Switch"; // Logged as onSwitchUser
|
||||
private static final String USER_STOPPING = "Stop"; // Logged as onStopUser
|
||||
private static final String USER_STOPPED = "Cleanup"; // Logged as onCleanupUser
|
||||
private static final String USER_COMPLETED_EVENT = "CompletedEvent"; // onCompletedEventUser
|
||||
private static final String USER_STARTING = "Start"; // Logged as onUserStarting()
|
||||
private static final String USER_UNLOCKING = "Unlocking"; // Logged as onUserUnlocking()
|
||||
private static final String USER_UNLOCKED = "Unlocked"; // Logged as onUserUnlocked()
|
||||
private static final String USER_SWITCHING = "Switch"; // Logged as onUserSwitching()
|
||||
private static final String USER_STOPPING = "Stop"; // Logged as onUserStopping()
|
||||
private static final String USER_STOPPED = "Cleanup"; // Logged as onUserStopped()
|
||||
private static final String USER_COMPLETED_EVENT = "CompletedEvent"; // onUserCompletedEvent()
|
||||
private static final String USER_VISIBLE = "Visible"; // Logged on onUserVisible() and
|
||||
// onUserStarting() (when visible is true)
|
||||
private static final String USER_INVISIBLE = "Invisible"; // Logged on onUserStopping()
|
||||
// (when visibilityChanged is true)
|
||||
|
||||
// The default number of threads to use if lifecycle thread pool is enabled.
|
||||
private static final int DEFAULT_MAX_USER_POOL_THREADS = 3;
|
||||
@@ -350,17 +354,40 @@ public final class SystemServiceManager implements Dumpable {
|
||||
/**
|
||||
* Starts the given user.
|
||||
*/
|
||||
public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
|
||||
EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId);
|
||||
public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId,
|
||||
boolean visible) {
|
||||
EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId, visible ? 1 : 0);
|
||||
|
||||
final TargetUser targetUser = newTargetUser(userId);
|
||||
synchronized (mTargetUsers) {
|
||||
mTargetUsers.put(userId, targetUser);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// Must send the user visiiblity change first, for 2 reasons:
|
||||
// 1. Automotive need to update the user-zone mapping ASAP and it's one of the few
|
||||
// services listening to this event (OTOH, there are manyy listeners to USER_STARTING
|
||||
// and some can take a while to process it)
|
||||
// 2. When a user is switched from bg to fg, the onUserVisibilityChanged() callback is
|
||||
// called onUserSwitching(), so calling it before onUserStarting() make it more
|
||||
// consistent with that
|
||||
onUser(t, USER_VISIBLE, /* prevUser= */ null, targetUser);
|
||||
}
|
||||
onUser(t, USER_STARTING, /* prevUser= */ null, targetUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user visibility.
|
||||
*
|
||||
* <p><b>NOTE: </b>this method should only be called when a user that is already running become
|
||||
* visible; if the user is starting visible, callers should call
|
||||
* {@link #onUserStarting(TimingsTraceAndSlog, int, boolean)} instead
|
||||
*/
|
||||
public void onUserVisible(@UserIdInt int userId) {
|
||||
EventLog.writeEvent(EventLogTags.SSM_USER_VISIBLE, userId);
|
||||
onUser(USER_VISIBLE, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the given user.
|
||||
*/
|
||||
@@ -408,9 +435,12 @@ public final class SystemServiceManager implements Dumpable {
|
||||
/**
|
||||
* Stops the given user.
|
||||
*/
|
||||
public void onUserStopping(@UserIdInt int userId) {
|
||||
EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId);
|
||||
public void onUserStopping(@UserIdInt int userId, boolean visibilityChanged) {
|
||||
EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId, visibilityChanged ? 1 : 0);
|
||||
onUser(USER_STOPPING, userId);
|
||||
if (visibilityChanged) {
|
||||
onUser(USER_INVISIBLE, userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,13 +486,12 @@ public final class SystemServiceManager implements Dumpable {
|
||||
TargetUser targetUser = getTargetUser(userId);
|
||||
Preconditions.checkState(targetUser != null, "No TargetUser for " + userId);
|
||||
|
||||
onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, /* prevUser= */ null,
|
||||
targetUser);
|
||||
onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, /* prevUser= */ null, targetUser);
|
||||
}
|
||||
|
||||
private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
|
||||
@Nullable TargetUser prevUser, @NonNull TargetUser curUser) {
|
||||
onUser(t, onWhat, prevUser, curUser, /* completedEventType=*/ null);
|
||||
onUser(t, onWhat, prevUser, curUser, /* completedEventType= */ null);
|
||||
}
|
||||
|
||||
private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
|
||||
@@ -534,6 +563,12 @@ public final class SystemServiceManager implements Dumpable {
|
||||
threadPool.submit(getOnUserCompletedEventRunnable(
|
||||
t, service, serviceName, curUser, completedEventType));
|
||||
break;
|
||||
case USER_VISIBLE:
|
||||
service.onUserVisibilityChanged(curUser, /* visible= */ true);
|
||||
break;
|
||||
case USER_INVISIBLE:
|
||||
service.onUserVisibilityChanged(curUser, /* visible= */ false);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(onWhat + " what?");
|
||||
}
|
||||
|
||||
@@ -8342,14 +8342,14 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
|
||||
Integer.toString(currentUserId), currentUserId);
|
||||
|
||||
// On Automotive, at this point the system user has already been started and unlocked,
|
||||
// and some of the tasks we do here have already been done. So skip those in that case.
|
||||
// TODO(b/132262830, b/203885241): this workdound shouldn't be necessary once we move the
|
||||
// headless-user start logic to UserManager-land
|
||||
// On Automotive / Headless System User Mode, at this point the system user has already been
|
||||
// started and unlocked, and some of the tasks we do here have already been done. So skip
|
||||
// those in that case.
|
||||
// TODO(b/242195409): this workaround shouldn't be necessary once we move the headless-user
|
||||
// start logic to UserManager-land
|
||||
final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
|
||||
|
||||
if (bootingSystemUser) {
|
||||
mSystemServiceManager.onUserStarting(t, currentUserId);
|
||||
mUserController.onSystemUserStarting();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
|
||||
@@ -101,7 +101,7 @@ option java_package com.android.server.am
|
||||
30073 uc_finish_user_stopping (userId|1|5)
|
||||
30074 uc_finish_user_stopped (userId|1|5)
|
||||
30075 uc_switch_user (userId|1|5)
|
||||
30076 uc_start_user_internal (userId|1|5)
|
||||
30076 uc_start_user_internal (userId|1|5),(foreground|1),(displayId|1|5)
|
||||
30077 uc_unlock_user (userId|1|5)
|
||||
30078 uc_finish_user_boot (userId|1|5)
|
||||
30079 uc_dispatch_user_switch (oldUserId|1|5),(newUserId|1|5)
|
||||
@@ -109,13 +109,14 @@ option java_package com.android.server.am
|
||||
30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)
|
||||
# Tags below are used by SystemServiceManager - although it's technically part of am, these are
|
||||
# also user switch events and useful to be analyzed together with events above.
|
||||
30082 ssm_user_starting (userId|1|5)
|
||||
30082 ssm_user_starting (userId|1|5),(visible|1)
|
||||
30083 ssm_user_switching (oldUserId|1|5),(newUserId|1|5)
|
||||
30084 ssm_user_unlocking (userId|1|5)
|
||||
30085 ssm_user_unlocked (userId|1|5)
|
||||
30086 ssm_user_stopping (userId|1|5)
|
||||
30086 ssm_user_stopping (userId|1|5),(visibilityChanged|1)
|
||||
30087 ssm_user_stopped (userId|1|5)
|
||||
30088 ssm_user_completed_event (userId|1|5),(eventFlag|1|5)
|
||||
30089 ssm_user_visible (userId|1|5)
|
||||
|
||||
# Foreground service start/stop events.
|
||||
30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
|
||||
|
||||
@@ -100,6 +100,7 @@ import android.util.EventLog;
|
||||
import android.util.IntArray;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.Display;
|
||||
@@ -174,6 +175,9 @@ class UserController implements Handler.Callback {
|
||||
static final int START_USER_SWITCH_FG_MSG = 120;
|
||||
static final int COMPLETE_USER_SWITCH_MSG = 130;
|
||||
static final int USER_COMPLETED_EVENT_MSG = 140;
|
||||
static final int USER_VISIBLE_MSG = 150;
|
||||
|
||||
private static final int NO_ARG2 = 0;
|
||||
|
||||
// Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
|
||||
// the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
|
||||
@@ -421,6 +425,17 @@ class UserController implements Handler.Callback {
|
||||
/** @see #getLastUserUnlockingUptime */
|
||||
private volatile long mLastUserUnlockingUptime = 0;
|
||||
|
||||
/**
|
||||
* List of visible users (as defined by {@link UserManager#isUserVisible()}).
|
||||
*
|
||||
* <p>It's only used to call {@link SystemServiceManager} when the visibility is changed upon
|
||||
* the user starting or stopping.
|
||||
*
|
||||
* <p>Note: only the key is used, not the value.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private final SparseBooleanArray mVisibleUsers = new SparseBooleanArray();
|
||||
|
||||
UserController(ActivityManagerService service) {
|
||||
this(new Injector(service));
|
||||
}
|
||||
@@ -1050,11 +1065,27 @@ class UserController implements Handler.Callback {
|
||||
// instead.
|
||||
userManagerInternal.unassignUserFromDisplay(userId);
|
||||
|
||||
final boolean visibilityChanged;
|
||||
boolean visibleBefore;
|
||||
synchronized (mLock) {
|
||||
visibleBefore = mVisibleUsers.get(userId);
|
||||
if (visibleBefore) {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.d(TAG, "Removing %d from mVisibleUsers", userId);
|
||||
}
|
||||
mVisibleUsers.delete(userId);
|
||||
visibilityChanged = true;
|
||||
} else {
|
||||
visibilityChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
updateStartedUserArrayLU();
|
||||
|
||||
final boolean allowDelayedLockingCopied = allowDelayedLocking;
|
||||
Runnable finishUserStoppingAsync = () ->
|
||||
mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));
|
||||
mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied,
|
||||
visibilityChanged));
|
||||
|
||||
if (mInjector.getUserManager().isPreCreated(userId)) {
|
||||
finishUserStoppingAsync.run();
|
||||
@@ -1092,7 +1123,7 @@ class UserController implements Handler.Callback {
|
||||
}
|
||||
|
||||
private void finishUserStopping(final int userId, final UserState uss,
|
||||
final boolean allowDelayedLocking) {
|
||||
final boolean allowDelayedLocking, final boolean visibilityChanged) {
|
||||
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
|
||||
synchronized (mLock) {
|
||||
if (uss.state != UserState.STATE_STOPPING) {
|
||||
@@ -1109,7 +1140,7 @@ class UserController implements Handler.Callback {
|
||||
mInjector.batteryStatsServiceNoteEvent(
|
||||
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
|
||||
Integer.toString(userId), userId);
|
||||
mInjector.getSystemServiceManager().onUserStopping(userId);
|
||||
mInjector.getSystemServiceManager().onUserStopping(userId, visibilityChanged);
|
||||
|
||||
Runnable finishUserStoppedAsync = () ->
|
||||
mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
|
||||
@@ -1513,16 +1544,17 @@ class UserController implements Handler.Callback {
|
||||
private boolean startUserInternal(@UserIdInt int userId, int displayId, boolean foreground,
|
||||
@Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.i(TAG, "Starting user %d on display %d %s", userId, displayId,
|
||||
Slogf.i(TAG, "Starting user %d on display %d%s", userId, displayId,
|
||||
foreground ? " in foreground" : "");
|
||||
}
|
||||
|
||||
if (displayId != Display.DEFAULT_DISPLAY) {
|
||||
boolean onSecondaryDisplay = displayId != Display.DEFAULT_DISPLAY;
|
||||
if (onSecondaryDisplay) {
|
||||
Preconditions.checkArgument(!foreground, "Cannot start user %d in foreground AND "
|
||||
+ "on secondary display (%d)", userId, displayId);
|
||||
}
|
||||
// TODO(b/239982558): log display id (or use a new event)
|
||||
EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);
|
||||
EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId, foreground ? 1 : 0,
|
||||
displayId);
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
@@ -1571,8 +1603,9 @@ class UserController implements Handler.Callback {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (foreground && userInfo.preCreated) {
|
||||
Slogf.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
|
||||
if ((foreground || onSecondaryDisplay) && userInfo.preCreated) {
|
||||
Slogf.w(TAG, "Cannot start pre-created user #" + userId + " in foreground or on "
|
||||
+ "secondary display");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1656,6 +1689,28 @@ class UserController implements Handler.Callback {
|
||||
}
|
||||
t.traceEnd();
|
||||
|
||||
// Need to call UM when user is on background, as there are some cases where the user
|
||||
// cannot be started in background on a secondary display (for example, if user is a
|
||||
// profile).
|
||||
// TODO(b/253103846): it's also explicitly checking if the user is the USER_SYSTEM, as
|
||||
// the UM call would return true during boot (when CarService / BootUserInitializer
|
||||
// calls AM.startUserInBackground() because the system user is still the current user.
|
||||
// TODO(b/244644281): another fragility of this check is that it must wait to call
|
||||
// UMI.isUserVisible() until the user state is check, as that method checks if the
|
||||
// profile of the current user is started. We should fix that dependency so the logic
|
||||
// belongs to just one place (like UserDisplayAssigner)
|
||||
boolean visible = foreground
|
||||
|| userId != UserHandle.USER_SYSTEM
|
||||
&& mInjector.getUserManagerInternal().isUserVisible(userId);
|
||||
if (visible) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.d(TAG, "Adding %d to mVisibleUsers", userId);
|
||||
}
|
||||
mVisibleUsers.put(userId, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure user is in the started state. If it is currently
|
||||
// stopping, we need to knock that off.
|
||||
if (uss.state == UserState.STATE_STOPPING) {
|
||||
@@ -1692,8 +1747,15 @@ class UserController implements Handler.Callback {
|
||||
// Booting up a new user, need to tell system services about it.
|
||||
// Note that this is on the same handler as scheduling of broadcasts,
|
||||
// which is important because it needs to go first.
|
||||
mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
|
||||
mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId,
|
||||
visible ? 1 : 0));
|
||||
t.traceEnd();
|
||||
} else if (visible) {
|
||||
// User was already running and became visible (for example, when switching to a
|
||||
// user that was started in the background before), so it's necessary to explicitly
|
||||
// notify the services (while when the user starts from BOOTING, USER_START_MSG
|
||||
// takes care of that.
|
||||
mHandler.sendMessage(mHandler.obtainMessage(USER_VISIBLE_MSG, userId, NO_ARG2));
|
||||
}
|
||||
|
||||
t.traceBegin("sendMessages");
|
||||
@@ -2110,6 +2172,11 @@ class UserController implements Handler.Callback {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
|
||||
stopGuestOrEphemeralUserIfBackground(oldUserId);
|
||||
stopUserOnSwitchIfEnforced(oldUserId);
|
||||
if (oldUserId == UserHandle.USER_SYSTEM) {
|
||||
// System user is never stopped, but its visibility is changed (as it is brought to the
|
||||
// background)
|
||||
updateSystemUserVisibility(/* visible= */ false);
|
||||
}
|
||||
|
||||
t.traceEnd(); // end continueUserSwitch
|
||||
}
|
||||
@@ -2413,9 +2480,7 @@ class UserController implements Handler.Callback {
|
||||
void setAllowUserUnlocking(boolean allowed) {
|
||||
mAllowUserUnlocking = allowed;
|
||||
if (DEBUG_MU) {
|
||||
// TODO(b/245335748): use Slogf.d instead
|
||||
// Slogf.d(TAG, new Exception(), "setAllowUserUnlocking(%b)", allowed);
|
||||
android.util.Slog.d(TAG, "setAllowUserUnlocking():" + allowed, new Exception());
|
||||
Slogf.d(TAG, new Exception(), "setAllowUserUnlocking(%b)", allowed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2457,10 +2522,34 @@ class UserController implements Handler.Callback {
|
||||
}
|
||||
|
||||
void onSystemReady() {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.d(TAG, "onSystemReady()");
|
||||
|
||||
}
|
||||
updateCurrentProfileIds();
|
||||
mInjector.reportCurWakefulnessUsageEvent();
|
||||
}
|
||||
|
||||
// TODO(b/242195409): remove this method if initial system user boot logic is refactored?
|
||||
void onSystemUserStarting() {
|
||||
updateSystemUserVisibility(/* visible= */ !UserManager.isHeadlessSystemUserMode());
|
||||
}
|
||||
|
||||
private void updateSystemUserVisibility(boolean visible) {
|
||||
if (DEBUG_MU) {
|
||||
Slogf.d(TAG, "updateSystemUserVisibility(): visible=%b", visible);
|
||||
}
|
||||
int userId = UserHandle.USER_SYSTEM;
|
||||
synchronized (mLock) {
|
||||
if (visible) {
|
||||
mVisibleUsers.put(userId, true);
|
||||
} else {
|
||||
mVisibleUsers.delete(userId);
|
||||
}
|
||||
}
|
||||
mInjector.onUserStarting(userId, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the list of users related to the current user when either a
|
||||
* user switch happens or when a new related user is started in the
|
||||
@@ -2846,6 +2935,9 @@ class UserController implements Handler.Callback {
|
||||
proto.end(uToken);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mVisibleUsers.size(); i++) {
|
||||
proto.write(UserControllerProto.VISIBLE_USERS_ARRAY, mVisibleUsers.keyAt(i));
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
}
|
||||
@@ -2899,7 +2991,8 @@ class UserController implements Handler.Callback {
|
||||
if (mSwitchingToSystemUserMessage != null) {
|
||||
pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
|
||||
}
|
||||
pw.println(" mLastUserUnlockingUptime:" + mLastUserUnlockingUptime);
|
||||
pw.println(" mLastUserUnlockingUptime: " + mLastUserUnlockingUptime);
|
||||
pw.println(" mVisibleUsers: " + mVisibleUsers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2936,8 +3029,7 @@ class UserController implements Handler.Callback {
|
||||
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER,
|
||||
USER_LIFECYCLE_EVENT_STATE_BEGIN);
|
||||
|
||||
mInjector.getSystemServiceManager().onUserStarting(
|
||||
TimingsTraceAndSlog.newAsyncLog(), msg.arg1);
|
||||
mInjector.onUserStarting(/* userId= */ msg.arg1, /* visible= */ msg.arg2 == 1);
|
||||
scheduleOnUserCompletedEvent(msg.arg1,
|
||||
UserCompletedEventType.EVENT_TYPE_USER_STARTING,
|
||||
USER_COMPLETED_EVENT_DELAY_MS);
|
||||
@@ -3018,6 +3110,9 @@ class UserController implements Handler.Callback {
|
||||
case COMPLETE_USER_SWITCH_MSG:
|
||||
completeUserSwitch(msg.arg1);
|
||||
break;
|
||||
case USER_VISIBLE_MSG:
|
||||
mInjector.getSystemServiceManager().onUserVisible(/* userId= */ msg.arg1);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -3531,5 +3626,10 @@ class UserController implements Handler.Callback {
|
||||
boolean isUsersOnSecondaryDisplaysEnabled() {
|
||||
return UserManager.isUsersOnSecondaryDisplaysEnabled();
|
||||
}
|
||||
|
||||
void onUserStarting(int userId, boolean visible) {
|
||||
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId,
|
||||
visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
|
||||
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
|
||||
import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.UserHandle.USER_SYSTEM;
|
||||
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
@@ -246,7 +247,7 @@ public class UserControllerTest {
|
||||
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
|
||||
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
|
||||
|
||||
mUserController.startUser(TEST_USER_ID, true /* foreground */);
|
||||
mUserController.startUser(TEST_USER_ID, /* foreground= */ true);
|
||||
verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
|
||||
verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
|
||||
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
|
||||
@@ -258,6 +259,8 @@ public class UserControllerTest {
|
||||
assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true));
|
||||
// Make sure no intents have been fired for pre-created users.
|
||||
assertTrue(mInjector.mSentIntents.isEmpty());
|
||||
|
||||
verifyUserNeverAssignedToDisplay();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -280,6 +283,8 @@ public class UserControllerTest {
|
||||
// binder calls, but their side effects (in this case, that the user is stopped right away)
|
||||
assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
|
||||
.containsExactly(USER_START_MSG);
|
||||
|
||||
verifyUserAssignedToDisplay(TEST_PRE_CREATED_USER_ID, Display.DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
private void startUserAssertions(
|
||||
@@ -303,6 +308,7 @@ public class UserControllerTest {
|
||||
assertEquals("User must be in STATE_BOOTING", UserState.STATE_BOOTING, userState.state);
|
||||
assertEquals("Unexpected old user id", 0, reportMsg.arg1);
|
||||
assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
|
||||
verifyUserAssignedToDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,6 +319,8 @@ public class UserControllerTest {
|
||||
mUserController.startUserInForeground(NONEXIST_USER_ID);
|
||||
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
|
||||
verify(mInjector.getWindowManager()).setSwitchingUser(false);
|
||||
|
||||
verifyUserNeverAssignedToDisplay();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -395,6 +403,7 @@ public class UserControllerTest {
|
||||
verify(mInjector, times(0)).dismissKeyguard(any(), anyString());
|
||||
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
|
||||
continueUserSwitchAssertions(TEST_USER_ID, false);
|
||||
verifyOnUserStarting(USER_SYSTEM, /* visible= */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -403,7 +412,7 @@ public class UserControllerTest {
|
||||
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
|
||||
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
|
||||
// Start user -- this will update state of mUserController
|
||||
mUserController.startUser(TEST_USER_ID, true);
|
||||
mUserController.startUser(TEST_USER_ID, /* foreground=*/ true);
|
||||
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
|
||||
assertNotNull(reportMsg);
|
||||
UserState userState = (UserState) reportMsg.obj;
|
||||
@@ -415,6 +424,7 @@ public class UserControllerTest {
|
||||
verify(mInjector, times(1)).dismissKeyguard(any(), anyString());
|
||||
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
|
||||
continueUserSwitchAssertions(TEST_USER_ID, false);
|
||||
verifyOnUserStarting(USER_SYSTEM, /* visible= */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -423,7 +433,7 @@ public class UserControllerTest {
|
||||
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
|
||||
|
||||
// Start user -- this will update state of mUserController
|
||||
mUserController.startUser(TEST_USER_ID, true);
|
||||
mUserController.startUser(TEST_USER_ID, /* foreground=*/ true);
|
||||
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
|
||||
assertNotNull(reportMsg);
|
||||
UserState userState = (UserState) reportMsg.obj;
|
||||
@@ -521,6 +531,7 @@ public class UserControllerTest {
|
||||
assertFalse(mUserController.canStartMoreUsers());
|
||||
assertEquals(Arrays.asList(new Integer[] {0, TEST_USER_ID1, TEST_USER_ID2}),
|
||||
mUserController.getRunningUsersLU());
|
||||
verifyOnUserStarting(USER_SYSTEM, /* visible= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,7 +541,7 @@ public class UserControllerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testUserLockingFromUserSwitchingForMultipleUsersDelayedLockingMode()
|
||||
throws InterruptedException, RemoteException {
|
||||
throws Exception {
|
||||
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
|
||||
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true);
|
||||
|
||||
@@ -645,6 +656,8 @@ public class UserControllerTest {
|
||||
setUpUser(TEST_USER_ID1, 0);
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> mUserController.startProfile(TEST_USER_ID1));
|
||||
|
||||
verifyUserNeverAssignedToDisplay();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -660,6 +673,8 @@ public class UserControllerTest {
|
||||
setUpUser(TEST_USER_ID1, UserInfo.FLAG_PROFILE | UserInfo.FLAG_DISABLED, /* preCreated= */
|
||||
false, UserManager.USER_TYPE_PROFILE_MANAGED);
|
||||
assertThat(mUserController.startProfile(TEST_USER_ID1)).isFalse();
|
||||
|
||||
verifyUserNeverAssignedToDisplay();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -949,6 +964,10 @@ public class UserControllerTest {
|
||||
verify(mInjector.getUserManagerInternal(), never()).unassignUserFromDisplay(userId);
|
||||
}
|
||||
|
||||
private void verifyOnUserStarting(@UserIdInt int userId, boolean visible) {
|
||||
verify(mInjector).onUserStarting(userId, visible);
|
||||
}
|
||||
|
||||
// Should be public to allow mocking
|
||||
private static class TestInjector extends UserController.Injector {
|
||||
public final TestHandler mHandler;
|
||||
@@ -1084,6 +1103,11 @@ public class UserControllerTest {
|
||||
protected LockPatternUtils getLockPatternUtils() {
|
||||
return mLockPatternUtilsMock;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onUserStarting(@UserIdInt int userId, boolean visible) {
|
||||
Log.i(TAG, "onUserStarting(" + userId + ", " + visible + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestHandler extends Handler {
|
||||
|
||||
Reference in New Issue
Block a user