blob: a6b532cdef0942bc14c040e7dc3d5d3842e82415 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.am;
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.Manifest.permission.MANAGE_USERS;
import static android.app.ActivityManager.STOP_USER_ON_SWITCH_DEFAULT;
import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
import static android.app.ActivityManager.StopUserOnSwitch;
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;
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
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.os.PowerWhitelistManager.REASON_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
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.ActivityManagerService.MY_PID;
import static com.android.server.am.UserState.STATE_BOOTING;
import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INVALID_SESSION_ID;
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_BEGIN;
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_FINISH;
import static com.android.server.pm.UserJourneyLogger.EVENT_STATE_NONE;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_START;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_STOP;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_FG;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_SWITCH_UI;
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKED_USER;
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_UNLOCKING_USER;
import static com.android.server.pm.UserJourneyLogger.USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND_VISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_FOREGROUND;
import static com.android.server.pm.UserManagerInternal.userAssignmentResultToString;
import static com.android.server.pm.UserManagerInternal.userStartModeToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
import android.app.usage.UsageEvents;
import android.appwidget.AppWidgetManagerInternal;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.PermissionChecker;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackagePartitions;
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.IRemoteCallback;
import android.os.IUserManager;
import android.os.Message;
import android.os.PowerWhitelistManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ObjectUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FactoryResetter;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService.UserCompletedEventType;
import com.android.server.SystemServiceManager;
import com.android.server.am.UserState.KeyEvictedCallback;
import com.android.server.pm.UserJourneyLogger;
import com.android.server.pm.UserJourneyLogger.UserJourneySession;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.pm.UserManagerInternal.UserStartMode;
import com.android.server.pm.UserManagerService;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
*
* <p>This class use {@link #mLock} to synchronize access to internal state. Methods that require
* {@link #mLock} to be held should have "LU" suffix in the name.
*
* <p><strong>Important:</strong> Synchronized code, i.e. one executed inside a synchronized(mLock)
* block or inside LU method, should only access internal state of this class or make calls to
* other LU methods. Non-LU method calls or calls to external classes are discouraged as they
* may cause lock inversion.
*/
class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
// Amount of time we wait for observers to handle a user switch before
// giving up on them and dismissing the user switching dialog.
static final int DEFAULT_USER_SWITCH_TIMEOUT_MS = 3 * 1000;
/**
* Amount of time we wait for an observer to handle a user switch before we log a warning. This
* wait time is per observer.
*/
private static final int LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS = 500;
// ActivityManager thread message constants
static final int REPORT_USER_SWITCH_MSG = 10;
static final int CONTINUE_USER_SWITCH_MSG = 20;
static final int USER_SWITCH_TIMEOUT_MSG = 30;
static final int START_PROFILES_MSG = 40;
static final int USER_START_MSG = 50;
static final int USER_CURRENT_MSG = 60;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
static final int USER_UNLOCK_MSG = 100;
static final int USER_UNLOCKED_MSG = 105;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
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;
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
// complete within {@link USER_JOURNEY_TIMEOUT}.
static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200;
// Wait time for completing the user journey. If a user journey is not complete within this
// time, the remaining lifecycle events for the journey would not be logged in statsd.
// Timeout set for 90 seconds.
private static final int USER_JOURNEY_TIMEOUT_MS = 90_000;
// UI thread message constants
static final int START_USER_SWITCH_UI_MSG = 1000;
/**
* If a callback wasn't called within USER_SWITCH_CALLBACKS_TIMEOUT_MS after
* {@link #getUserSwitchTimeoutMs}, an error is reported. Usually it indicates a problem in the
* observer when it never calls back.
*/
private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000;
/**
* Amount of time waited for {@link WindowManagerService#dismissKeyguard} callbacks to be
* called after dismissing the keyguard.
* Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog()}
* and report user switch is complete {@link #REPORT_USER_SWITCH_COMPLETE_MSG}.
*/
private static final int DISMISS_KEYGUARD_TIMEOUT_MS = 2 * 1000;
/**
* Time after last scheduleOnUserCompletedEvent() call at which USER_COMPLETED_EVENT_MSG will be
* scheduled (although it may fire sooner instead).
* When it fires, {@link #reportOnUserCompletedEvent} will be processed.
*/
// TODO(b/197344658): Increase to 10s or 15s once we have a switch-UX-is-done invocation too.
private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000;
/**
* Maximum number of users we allow to be running at a time, including system user.
*
* <p>This parameter only affects how many background users will be stopped when switching to a
* new user. It has no impact on {@link #startUser(int, boolean)} behavior.
*
* <p>Note: Current and system user (and their related profiles) are never stopped when
* switching users. Due to that, the actual number of running users can exceed mMaxRunningUsers
*/
@GuardedBy("mLock")
private int mMaxRunningUsers;
// Lock for internal state.
private final Object mLock = new Object();
private final Injector mInjector;
private final Handler mHandler;
private final Handler mUiHandler;
// Holds the current foreground user's id. Use mLock when updating
@GuardedBy("mLock")
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
// Holds the target user's id during a user switch. The value of mCurrentUserId will be updated
// once target user goes into the foreground. Use mLock when updating
@GuardedBy("mLock")
private volatile int mTargetUserId = UserHandle.USER_NULL;
// If a user switch request comes during an ongoing user switch, it is postponed to the end of
// the current switch, and this variable holds those user ids. Use mLock when updating
@GuardedBy("mLock")
private final ArrayDeque<Integer> mPendingTargetUserIds = new ArrayDeque<>();
/**
* Which users have been started, so are allowed to run code.
*/
@GuardedBy("mLock")
private final SparseArray<UserState> mStartedUsers = new SparseArray<>();
/**
* LRU list of history of current users. Most recently current is at the end.
*/
@GuardedBy("mLock")
private final ArrayList<Integer> mUserLru = new ArrayList<>();
/**
* Constant array of the users that are currently started.
*/
@GuardedBy("mLock")
private int[] mStartedUserArray = new int[] { 0 };
/**
* Contains the current user and its profiles (if any).
*
* <p><b>NOTE: </b>it lists all profiles, regardless of their running state (i.e., they're in
* this list even if not running).
*/
@GuardedBy("mLock")
private int[] mCurrentProfileIds = new int[] {};
/**
* Mapping from each known user ID to the profile group ID it is associated with.
* <p>Users not present in this array have a profile group of NO_PROFILE_GROUP_ID.
*/
@GuardedBy("mLock")
private final SparseIntArray mUserProfileGroupIds = new SparseIntArray();
/**
* Registered observers of the user switching mechanics.
*/
private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
= new RemoteCallbackList<>();
@GuardedBy("mLock")
private boolean mUserSwitchUiEnabled = true;
/**
* Currently active user switch callbacks.
*/
@GuardedBy("mLock")
private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
/**
* Messages for switching from {@link android.os.UserHandle#SYSTEM}.
*/
@GuardedBy("mLock")
private String mSwitchingFromSystemUserMessage;
/**
* Messages for switching to {@link android.os.UserHandle#SYSTEM}.
*/
@GuardedBy("mLock")
private String mSwitchingToSystemUserMessage;
/**
* Callbacks that are still active after {@link #getUserSwitchTimeoutMs}
*/
@GuardedBy("mLock")
private ArraySet<String> mTimeoutUserSwitchCallbacks;
private final LockPatternUtils mLockPatternUtils;
// TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for
// the system user in HSUM.
@GuardedBy("mLock")
private boolean mIsBroadcastSentForSystemUserStarted;
// TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for
// the system user in HSUM.
@GuardedBy("mLock")
private boolean mIsBroadcastSentForSystemUserStarting;
volatile boolean mBootCompleted;
/**
* In this mode, user is always stopped when switched out (unless overridden by the
* {@code fw.stop_bg_users_on_switch} system property) but locking of user data is
* postponed until total number of unlocked users in the system reaches mMaxRunningUsers.
* Once total number of unlocked users reach mMaxRunningUsers, least recently used user
* will be locked.
*/
// TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
// delayed locking behavior once the private space flag is finalized.
@GuardedBy("mLock")
private boolean mDelayUserDataLocking;
/**
* Users are only allowed to be unlocked after boot complete.
*/
private volatile boolean mAllowUserUnlocking;
/**
* Keep track of last active users for delayUserDataLocking.
* The most recently stopped user with delayed locking is placed in front, while the least
* recently stopped user in back.
*/
@GuardedBy("mLock")
private final ArrayList<Integer> mLastActiveUsersForDelayedLocking = new ArrayList<>();
/**
* Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet-
* unreported user-starting events have transpired for the given user.
*/
@GuardedBy("mCompletedEventTypes")
private final SparseIntArray mCompletedEventTypes = new SparseIntArray();
/**
* Sets on {@link #setInitialConfig(boolean, int, boolean)}, which is called by
* {@code ActivityManager} when the system is started.
*
* <p>It's useful to ignore external operations (i.e., originated outside {@code system_server},
* like from {@code adb shell am switch-user})) that could happen before such call is made and
* the system is ready.
*/
@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 @StopUserOnSwitch int mStopUserOnSwitch = STOP_USER_ON_SWITCH_DEFAULT;
/** @see #getLastUserUnlockingUptime */
private volatile long mLastUserUnlockingUptime = 0;
/**
* Pending user starts waiting for shutdown step to complete.
*/
@GuardedBy("mLock")
private final List<PendingUserStart> mPendingUserStarts = new ArrayList<>();
private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
@Override
public void onUserCreated(UserInfo user, Object token) {
onUserAdded(user);
}
@Override
public void onUserRemoved(UserInfo user) {
UserController.this.onUserRemoved(user.id);
}
};
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@VisibleForTesting
UserController(Injector injector) {
mInjector = injector;
// This should be called early to avoid a null mHandler inside the injector
mHandler = mInjector.getHandler(this);
mUiHandler = mInjector.getUiHandler(this);
// User 0 is the first and only user that runs at boot.
final UserState uss = new UserState(UserHandle.SYSTEM);
uss.mUnlockProgress.addListener(new UserProgressListener());
mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
updateStartedUserArrayLU();
}
void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
boolean delayUserDataLocking) {
synchronized (mLock) {
mUserSwitchUiEnabled = userSwitchUiEnabled;
mMaxRunningUsers = maxRunningUsers;
mDelayUserDataLocking = delayUserDataLocking;
mInitialized = true;
}
}
private boolean isUserSwitchUiEnabled() {
synchronized (mLock) {
return mUserSwitchUiEnabled;
}
}
int getMaxRunningUsers() {
synchronized (mLock) {
return mMaxRunningUsers;
}
}
void setStopUserOnSwitch(@StopUserOnSwitch int value) {
if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
== PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS)
== PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"You either need MANAGE_USERS or INTERACT_ACROSS_USERS permission to "
+ "call setStopUserOnSwitch()");
}
synchronized (mLock) {
Slogf.i(TAG, "setStopUserOnSwitch(): %d -> %d", mStopUserOnSwitch, value);
mStopUserOnSwitch = value;
}
}
private boolean shouldStopUserOnSwitch() {
synchronized (mLock) {
if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
Slogf.i(TAG, "shouldStopUserOnSwitch(): 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;
}
void finishUserSwitch(UserState uss) {
// This call holds the AM lock so we post to the handler.
mHandler.post(() -> {
finishUserBoot(uss);
startProfiles();
stopExcessRunningUsers();
});
}
@GuardedBy("mLock")
@VisibleForTesting
List<Integer> getRunningUsersLU() {
ArrayList<Integer> runningUsers = new ArrayList<>();
for (Integer userId : mUserLru) {
UserState uss = mStartedUsers.get(userId);
if (uss == null) {
// Shouldn't happen, but recover if it does.
continue;
}
if (uss.state == UserState.STATE_STOPPING
|| uss.state == UserState.STATE_SHUTDOWN) {
// This user is already stopping, doesn't count.
continue;
}
runningUsers.add(userId);
}
return runningUsers;
}
private void stopExcessRunningUsers() {
final ArraySet<Integer> exemptedUsers = new ArraySet<>();
final List<UserInfo> users = mInjector.getUserManager().getUsers(true);
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
if (isAlwaysVisibleUser(userId)) {
exemptedUsers.add(userId);
}
}
synchronized (mLock) {
stopExcessRunningUsersLU(mMaxRunningUsers, exemptedUsers);
}
}
@GuardedBy("mLock")
private void stopExcessRunningUsersLU(int maxRunningUsers, ArraySet<Integer> exemptedUsers) {
List<Integer> currentlyRunning = getRunningUsersLU();
Iterator<Integer> iterator = currentlyRunning.iterator();
while (currentlyRunning.size() > maxRunningUsers && iterator.hasNext()) {
Integer userId = iterator.next();
if (userId == UserHandle.USER_SYSTEM
|| userId == mCurrentUserId
|| exemptedUsers.contains(userId)) {
// System and current users can't be stopped, and an exempt user shouldn't be
continue;
}
// allowDelayedLocking set here as stopping user is done without any explicit request
// from outside.
if (stopUsersLU(userId, /* force= */ false, /* allowDelayedLocking= */ true,
/* stopUserCallback= */ null, /* keyEvictedCallback= */ null)
== USER_OP_SUCCESS) {
iterator.remove();
}
}
}
/**
* Returns if more users can be started without stopping currently running users.
*/
boolean canStartMoreUsers() {
synchronized (mLock) {
return getRunningUsersLU().size() < mMaxRunningUsers;
}
}
private void finishUserBoot(UserState uss) {
finishUserBoot(uss, null);
}
private void finishUserBoot(UserState uss, IIntentReceiver resultTo) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_BOOT, userId);
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(userId) != uss) {
return;
}
}
// We always walk through all the user lifecycle states to send
// consistent developer events. We step into RUNNING_LOCKED here,
// but we might immediately step into RUNNING below if the user
// storage is already unlocked.
if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
mInjector.getUserJourneyLogger()
.logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
EVENT_STATE_NONE);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
final long elapsedTimeMs = SystemClock.elapsedRealtime();
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED,
elapsedTimeMs);
final long maxElapsedTimeMs = 120_000;
if (elapsedTimeMs > maxElapsedTimeMs) {
Slogf.wtf("SystemServerTiming",
"finishUserBoot took too long. elapsedTimeMs=" + elapsedTimeMs);
}
}
if (!mInjector.getUserManager().isPreCreated(userId)) {
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
userId, 0));
// The "locked boot complete" broadcast for the system user is supposed be sent when
// the device has finished booting. Normally, that is the same time that the system
// user transitions to RUNNING_LOCKED. However, in "headless system user mode", the
// system user is explicitly started before the device has finished booting. In
// that case, we need to wait until onBootComplete() to send the broadcast.
// Similarly, this occurs after a user switch, but in HSUM we switch to the main
// user before boot is complete, so again this should be delayed until
// onBootComplete if boot has not yet completed.
if (mAllowUserUnlocking) {
// ACTION_LOCKED_BOOT_COMPLETED
sendLockedBootCompletedBroadcast(resultTo, userId);
}
}
}
// We need to delay unlocking profiles until the parent user is also unlocked.
final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent == null) {
// Not a profile (or is a parentless profile) so no parent for which to wait.
maybeUnlockUser(userId);
} else if (isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
Slogf.d(TAG, "User " + userId + " (parent " + parent.id
+ "): attempting unlock because parent is unlocked");
maybeUnlockUser(userId);
} else {
Slogf.d(TAG, "User " + userId + " (parent " + parent.id
+ "): delaying unlock because parent is locked");
}
}
private void sendLockedBootCompletedBroadcast(IIntentReceiver receiver, @UserIdInt int userId) {
final Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
| Intent.FLAG_RECEIVER_OFFLOAD
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mInjector.broadcastIntent(intent, null, receiver, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
AppOpsManager.OP_NONE,
getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED)
.toBundle(), true,
false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(), userId);
}
/**
* Step from {@link UserState#STATE_RUNNING_LOCKED} to
* {@link UserState#STATE_RUNNING_UNLOCKING}.
*/
private boolean finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKING, userId);
mInjector.getUserJourneyLogger()
.logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
EVENT_STATE_BEGIN);
// If the user's CE storage hasn't been unlocked yet, we cannot proceed.
if (!StorageManager.isCeStorageUnlocked(userId)) return false;
synchronized (mLock) {
// Do not proceed if unexpected state or a stale user
if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
return false;
}
}
uss.mUnlockProgress.start();
// Prepare app storage before we go any further
uss.mUnlockProgress.setProgress(5,
mInjector.getContext().getString(R.string.android_start_title));
// Call onBeforeUnlockUser on a worker thread that allows disk I/O
FgThread.getHandler().post(() -> {
if (!StorageManager.isCeStorageUnlocked(userId)) {
Slogf.w(TAG, "User's CE storage got locked unexpectedly, leaving user locked.");
return;
}
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("UM.onBeforeUnlockUser-" + userId);
mInjector.getUserManager().onBeforeUnlockUser(userId);
t.traceEnd();
synchronized (mLock) {
// Do not proceed if unexpected state
if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
return;
}
}
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.setProgress(20);
mLastUserUnlockingUptime = SystemClock.uptimeMillis();
// Dispatch unlocked to system services; when fully dispatched,
// that calls through to the next "unlocked" phase
mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
});
return true;
}
/**
* Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
* {@link UserState#STATE_RUNNING_UNLOCKED}.
*/
private void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
// Only keep marching forward if the user's CE storage is unlocked.
if (!StorageManager.isCeStorageUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
// Do not proceed if unexpected state
if (!uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
return;
}
}
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.finish();
// Get unaware persistent apps running and start any unaware providers
// in already-running apps that are partially aware
if (userId == UserHandle.USER_SYSTEM) {
mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
}
mInjector.installEncryptionUnawareProviders(userId);
if (!mInjector.getUserManager().isPreCreated(userId)) {
// Dispatch unlocked to external apps
final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
unlockedIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,
null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(), userId);
}
final UserInfo userInfo = getUserInfo(userId);
if (userInfo.isProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
// Send PROFILE_ACCESSIBLE broadcast to the parent user if a profile was unlocked
broadcastProfileAccessibleStateChanged(userId, parent.id,
Intent.ACTION_PROFILE_ACCESSIBLE);
//TODO(b/175704931): send ACTION_MANAGED_PROFILE_AVAILABLE
// Also send MANAGED_PROFILE_UNLOCKED broadcast to the parent user
// if a managed profile was unlocked
if (userInfo.isManagedProfile()) {
final Intent profileUnlockedIntent = new Intent(
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
profileUnlockedIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
mInjector.broadcastIntent(profileUnlockedIntent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), parent.id);
}
}
}
// Send PRE_BOOT broadcasts if user fingerprint changed; we
// purposefully block sending BOOT_COMPLETED until after all
// PRE_BOOT receivers are finished to avoid ANR'ing apps
final UserInfo info = getUserInfo(userId);
if (!Objects.equals(info.lastLoggedInFingerprint, PackagePartitions.FINGERPRINT)
|| SystemProperties.getBoolean("persist.pm.mock-upgrade", false)) {
// Suppress double notifications for managed profiles that
// were unlocked automatically as part of their parent user being
// unlocked. TODO(b/217442918): this code doesn't work correctly.
final boolean quiet = info.isManagedProfile();
mInjector.sendPreBootBroadcast(userId, quiet,
() -> finishUserUnlockedCompleted(uss));
} else {
finishUserUnlockedCompleted(uss);
}
}
private void finishUserUnlockedCompleted(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED_COMPLETED, userId);
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
}
UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
return;
}
// Only keep marching forward if the user's CE storage is unlocked.
if (!StorageManager.isCeStorageUnlocked(userId)) return;
// Remember that we logged in
mInjector.getUserManager().onUserLoggedIn(userId);
Runnable initializeUser = () -> mInjector.getUserManager().makeInitialized(userInfo.id);
if (!userInfo.isInitialized()) {
Slogf.d(TAG, "Initializing user #" + userId);
if (userInfo.preCreated) {
initializeUser.run();
} else if (userId != UserHandle.USER_SYSTEM) {
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mInjector.broadcastIntent(intent, null,
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
// Note: performReceive is called with mService lock held
initializeUser.run();
}
}, 0, null, null, null, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), userId);
}
}
if (userInfo.preCreated) {
Slogf.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
// Pre-created user was started right after creation so services could properly
// intialize it; it should be stopped right away as it's not really a "real" user.
stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false,
/* stopUserCallback= */ null, /* keyEvictedCallback= */ null);
return;
}
// Spin up app widgets prior to boot-complete, so they can be ready promptly
mInjector.startUserWidgets(userId);
mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget();
Slogf.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
final long elapsedTimeMs = SystemClock.elapsedRealtime();
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED,
elapsedTimeMs);
}
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_OFFLOAD);
// Widget broadcasts are outbound via FgThread, so to guarantee sequencing
// we also send the boot_completed broadcast from that thread.
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
FgThread.getHandler().post(() -> {
mInjector.broadcastIntent(bootIntent, null,
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
Slogf.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
+ userId);
mBootCompleted = true;
}
}, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
AppOpsManager.OP_NONE,
getTemporaryAppAllowlistBroadcastOptions(REASON_BOOT_COMPLETED).toBundle(),
true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId);
});
}
int restartUser(final int userId, @UserStartMode int userStartMode) {
return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false,
/* stopUserCallback= */ null, new KeyEvictedCallback() {
@Override
public void keyEvicted(@UserIdInt int userId) {
// Post to the same handler that this callback is called from to ensure
// the user cleanup is complete before restarting.
mHandler.post(() -> UserController.this.startUser(userId, userStartMode));
}
});
}
/**
* Stops a user only if it's a profile, with a more relaxed permission requirement:
* {@link android.Manifest.permission#MANAGE_USERS} or
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
* To be called from ActivityManagerService.
* @param userId the id of the user to stop.
* @return true if the operation was successful.
*/
boolean stopProfile(final @UserIdInt int userId) {
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 "
+ "stop a profile");
}
final UserInfo userInfo = getUserInfo(userId);
if (userInfo == null || !userInfo.isProfile()) {
throw new IllegalArgumentException("User " + userId + " is not a profile");
}
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
return stopUsersLU(userId, /* force= */ true, /* allowDelayedLocking= */
false, /* stopUserCallback= */ null, /* keyEvictedCallback= */ null)
== ActivityManager.USER_OP_SUCCESS;
}
}
int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("UserController"
+ (force ? "-force" : "")
+ (allowDelayedLocking ? "-allowDelayedLocking" : "")
+ (stopUserCallback != null ? "-withStopUserCallback" : "")
+ "-" + userId + "-[stopUser]");
try {
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
keyEvictedCallback);
}
} finally {
t.traceEnd();
}
}
/**
* Stops the user along with its related users. The method calls
* {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
*/
@GuardedBy("mLock")
private int stopUsersLU(final int userId, boolean force, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
if (userId == UserHandle.USER_SYSTEM) {
return USER_OP_ERROR_IS_SYSTEM;
}
if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
for (int relatedUserId : usersToStop) {
if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) {
Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
}
// We still need to stop the requested user if it's a force stop.
if (force) {
Slogf.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
t.traceBegin("stopSingleUserLU-force-" + userId + "-[stopUser]");
stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
keyEvictedCallback);
t.traceEnd();
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
}
}
if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
t.traceBegin("stopSingleUserLU-" + userIdToStop + "-[stopUser]");
stopSingleUserLU(userIdToStop, allowDelayedLocking,
userIdToStop == userId ? stopUserCallback : null,
userIdToStop == userId ? keyEvictedCallback : null);
t.traceEnd();
}
return USER_OP_SUCCESS;
}
/**
* Stops a single User. This can also trigger locking user data out depending on device's
* config ({@code mDelayUserDataLocking}) and arguments.
* User will be unlocked when
* - {@code mDelayUserDataLocking} is not set.
* - {@code mDelayUserDataLocking} is set and {@code keyEvictedCallback} is non-null.
* -
*
* @param userId User Id to stop and lock the data.
* @param allowDelayedLocking When set, do not lock user after stopping. Locking can happen
* later when number of unlocked users reaches
* {@code mMaxRunnngUsers}. Note that this is respected only when
* {@code mDelayUserDataLocking} is set and {@keyEvictedCallback} is
* null. Otherwise the user will be locked.
* @param stopUserCallback Callback to notify that user has stopped.
* @param keyEvictedCallback Callback to notify that user has been unlocked.
*/
@GuardedBy("mLock")
private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback,
KeyEvictedCallback keyEvictedCallback) {
Slogf.i(TAG, "stopSingleUserLU userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) { // User is not started
// If canDelayDataLockingForUser() is true and allowDelayedLocking is false, we need
// to lock the requested user as the client wants to stop and lock the user. On the
// other hand, having keyEvictedCallback set will lead into locking user if
// canDelayDataLockingForUser() is true as that means client wants to lock the user
// immediately.
// If canDelayDataLockingForUser() is false, the user was already locked when it was
// stopped and no further action is necessary.
if (canDelayDataLockingForUser(userId)) {
if (allowDelayedLocking && keyEvictedCallback != null) {
Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+ " and lock user:" + userId, new RuntimeException());
allowDelayedLocking = false;
}
if (!allowDelayedLocking) {
if (mLastActiveUsersForDelayedLocking.remove(Integer.valueOf(userId))) {
// should lock the user, user is already gone
final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
if (keyEvictedCallback != null) {
keyEvictedCallbacks = new ArrayList<>(1);
keyEvictedCallbacks.add(keyEvictedCallback);
} else {
keyEvictedCallbacks = null;
}
dispatchUserLocking(userId, keyEvictedCallbacks);
}
}
}
// We do need to post the stopped callback even though user is already stopped.
if (stopUserCallback != null) {
mHandler.post(() -> {
try {
stopUserCallback.userStopped(userId);
} catch (RemoteException e) {
}
});
}
return;
}
logUserJourneyBegin(userId, USER_JOURNEY_USER_STOP);
if (stopUserCallback != null) {
uss.mStopCallbacks.add(stopUserCallback);
}
if (keyEvictedCallback != null) {
uss.mKeyEvictedCallbacks.add(keyEvictedCallback);
}
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("setUserState-STATE_STOPPING-" + userId + "-[stopUser]");
userManagerInternal.setUserState(userId, uss.state);
t.traceEnd();
t.traceBegin("unassignUserFromDisplayOnStop-" + userId + "-[stopUser]");
userManagerInternal.unassignUserFromDisplayOnStop(userId);
t.traceEnd();
updateStartedUserArrayLU();
final boolean allowDelayedLockingCopied = allowDelayedLocking;
Runnable finishUserStoppingAsync = () ->
mHandler.post(() -> {
TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
t2.traceBegin("finishUserStopping-" + userId + "-[stopUser]");
finishUserStopping(userId, uss, allowDelayedLockingCopied);
t2.traceEnd();
});
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppingAsync.run();
return;
}
// Post to handler to obtain amLock
mHandler.post(() -> {
// We are going to broadcast ACTION_USER_STOPPING and then
// once that is done send a final ACTION_SHUTDOWN and then
// stop the user.
final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING);
stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
// This is the result receiver for the initial stopping broadcast.
final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
asyncTraceEnd("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]",
userId);
finishUserStoppingAsync.run();
}
};
TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
t2.traceBegin("clearBroadcastQueueForUser-" + userId + "-[stopUser]");
// Clear broadcast queue for the user to avoid delivering stale broadcasts
mInjector.clearBroadcastQueueForUser(userId);
t2.traceEnd();
asyncTraceBegin("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]", userId);
// Kick things off.
mInjector.broadcastIntent(stoppingIntent,
null, stoppingReceiver, 0, null, null,
new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
});
}
}
private void finishUserStopping(final int userId, final UserState uss,
final boolean allowDelayedLocking) {
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
synchronized (mLock) {
if (uss.state != UserState.STATE_STOPPING) {
// Whoops, we are being started back up. Abort, abort!
UserJourneySession session = mInjector.getUserJourneyLogger()
.logUserJourneyFinishWithError(-1, getUserInfo(userId),
USER_JOURNEY_USER_STOP, ERROR_CODE_ABORTED);
if (session != null) {
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
} else {
mInjector.getUserJourneyLogger()
.logUserJourneyFinishWithError(-1, getUserInfo(userId),
USER_JOURNEY_USER_STOP, ERROR_CODE_INVALID_SESSION_ID);
}
return;
}
uss.setState(UserState.STATE_SHUTDOWN);
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
t.traceEnd();
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
Integer.toString(userId), userId);
mInjector.getSystemServiceManager().onUserStopping(userId);
Runnable finishUserStoppedAsync = () ->
mHandler.post(() -> {
TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
t2.traceBegin("finishUserStopped-" + userId + "-[stopUser]");
finishUserStopped(uss, allowDelayedLocking);
t2.traceEnd();
});
if (mInjector.getUserManager().isPreCreated(userId)) {
finishUserStoppedAsync.run();
return;
}
// Fire the shutdown intent.
final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
// This is the result receiver for the final shutdown broadcast.
final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
asyncTraceEnd("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
finishUserStoppedAsync.run();
}
};
asyncTraceBegin("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
mInjector.broadcastIntent(shutdownIntent,
null, shutdownReceiver, 0, null, null, null,
AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), userId);
}
@VisibleForTesting
void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
final int userId = uss.mHandle.getIdentifier();
if (DEBUG_MU) {
Slogf.i(TAG, "finishUserStopped(%d): allowDelayedLocking=%b", userId,
allowDelayedLocking);
}
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPED, userId);
final boolean stopped;
boolean lockUser = true;
final ArrayList<IStopUserCallback> stopCallbacks;
final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
int userIdToLock = userId;
// Must get a reference to UserInfo before it's removed
final UserInfo userInfo = getUserInfo(userId);
synchronized (mLock) {
stopCallbacks = new ArrayList<>(uss.mStopCallbacks);
keyEvictedCallbacks = new ArrayList<>(uss.mKeyEvictedCallbacks);
if (mStartedUsers.get(userId) != uss || uss.state != UserState.STATE_SHUTDOWN) {
stopped = false;
} else {
stopped = true;
// User can no longer run.
Slogf.i(TAG, "Removing user state from UserController.mStartedUsers for user #"
+ userId + " as a result of user being stopped");
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLU();
if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) {
Slogf.wtf(TAG,
"Delayed locking enabled while KeyEvictedCallbacks not empty, userId:"
+ userId + " callbacks:" + keyEvictedCallbacks);
allowDelayedLocking = false;
}
userIdToLock = updateUserToLockLU(userId, allowDelayedLocking);
if (userIdToLock == UserHandle.USER_NULL) {
lockUser = false;
}
}
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
if (stopped) {
Slogf.i(TAG, "Removing user state from UserManager.mUserStates for user #" + userId
+ " as a result of user being stopped");
mInjector.getUserManagerInternal().removeUserState(userId);
mInjector.activityManagerOnUserStopped(userId);
// Clean up all state and processes associated with the user.
// Kill all the processes for the user.
t.traceBegin("forceStopUser-" + userId + "-[stopUser]");
forceStopUser(userId, "finish user");
t.traceEnd();
}
for (final IStopUserCallback callback : stopCallbacks) {
try {
if (stopped) {
t.traceBegin("stopCallbacks.userStopped-" + userId + "-[stopUser]");
callback.userStopped(userId);
t.traceEnd();
} else {
t.traceBegin("stopCallbacks.userStopAborted-" + userId + "-[stopUser]");
callback.userStopAborted(userId);
t.traceEnd();
}
} catch (RemoteException ignored) {
}
}
if (stopped) {
t.traceBegin("systemServiceManagerOnUserStopped-" + userId + "-[stopUser]");
mInjector.systemServiceManagerOnUserStopped(userId);
t.traceEnd();
t.traceBegin("taskSupervisorRemoveUser-" + userId + "-[stopUser]");
mInjector.taskSupervisorRemoveUser(userId);
t.traceEnd();
// Remove the user if it is ephemeral.
if (userInfo.isEphemeral() && !userInfo.preCreated) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
UserJourneySession session = mInjector.getUserJourneyLogger()
.logUserJourneyFinish(-1, userInfo, USER_JOURNEY_USER_STOP);
if (session != null) {
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
}
if (lockUser) {
dispatchUserLocking(userIdToLock, keyEvictedCallbacks);
}
// Resume any existing pending user start,
// which was paused while the SHUTDOWN flow of the user was in progress.
resumePendingUserStarts(userId);
} else {
UserJourneySession session = mInjector.getUserJourneyLogger()
.finishAndClearIncompleteUserJourney(userId, USER_JOURNEY_USER_STOP);
if (session != null) {
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
}
}
}
/**
* Resume any existing pending user start for the specified userId which was paused
* while the shutdown flow of the user was in progress.
* Remove all the handled user starts from mPendingUserStarts.
* @param userId the id of the user
*/
private void resumePendingUserStarts(@UserIdInt int userId) {
synchronized (mLock) {
final List<PendingUserStart> handledUserStarts = new ArrayList<>();
for (PendingUserStart userStart: mPendingUserStarts) {
if (userStart.userId == userId) {
Slogf.i(TAG, "resumePendingUserStart for" + userStart);
mHandler.post(() -> startUser(userStart.userId,
userStart.userStartMode, userStart.unlockListener));
handledUserStarts.add(userStart);
}
}
// remove all the pending user starts which are now handled
mPendingUserStarts.removeAll(handledUserStarts);
}
}
private void dispatchUserLocking(@UserIdInt int userId,
@Nullable List<KeyEvictedCallback> keyEvictedCallbacks) {
// Evict the user's credential encryption key. Performed on FgThread to make it
// serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
// to prevent data corruption.
FgThread.getHandler().post(() -> {
synchronized (mLock) {
if (mStartedUsers.get(userId) != null) {
Slogf.w(TAG, "User was restarted, skipping key eviction");
return;
}
}
try {
Slogf.i(TAG, "Locking CE storage for user #" + userId);
mInjector.getStorageManager().lockCeStorage(userId);
} catch (RemoteException re) {
throw re.rethrowAsRuntimeException();
}
if (keyEvictedCallbacks == null) {
return;
}
for (int i = 0; i < keyEvictedCallbacks.size(); i++) {
keyEvictedCallbacks.get(i).keyEvicted(userId);
}
});
}
/**
* For mDelayUserDataLocking mode, storage once unlocked is kept unlocked.
* Total number of unlocked user storage is limited by mMaxRunningUsers.
* If there are more unlocked users, evict and lock the least recently stopped user and
* lock that user's data. Regardless of the mode, ephemeral user is always locked
* immediately.
*
* @return user id to lock. UserHandler.USER_NULL will be returned if no user should be locked.
*/
@GuardedBy("mLock")
private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) {
int userIdToLock = userId;
// TODO: Decouple the delayed locking flows from mMaxRunningUsers or rename the property to
// state maximum running unlocked users specifically
if (canDelayDataLockingForUser(userIdToLock) && allowDelayedLocking
&& !getUserInfo(userId).isEphemeral()
&& !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) {
// arg should be object, not index
mLastActiveUsersForDelayedLocking.remove((Integer) userId);
mLastActiveUsersForDelayedLocking.add(0, userId);
int totalUnlockedUsers = mStartedUsers.size()
+ mLastActiveUsersForDelayedLocking.size();
if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user
userIdToLock = mLastActiveUsersForDelayedLocking.get(
mLastActiveUsersForDelayedLocking.size() - 1);
mLastActiveUsersForDelayedLocking
.remove(mLastActiveUsersForDelayedLocking.size() - 1);
Slogf.i(TAG, "finishUserStopped, stopping user:" + userId
+ " lock user:" + userIdToLock);
} else {
Slogf.i(TAG, "finishUserStopped, user:" + userId + ", skip locking");
// do not lock
userIdToLock = UserHandle.USER_NULL;
}
}
return userIdToLock;
}
/**
* Returns whether the user can have its CE storage left unlocked, even when it is stopped,
* either due to a global device configuration or an individual user's property.
*/
private boolean canDelayDataLockingForUser(@UserIdInt int userIdToLock) {
if (allowBiometricUnlockForPrivateProfile()) {
final UserProperties userProperties = getUserProperties(userIdToLock);
return (mDelayUserDataLocking || (userProperties != null
&& userProperties.getAllowStoppingUserWithDelayedLocking()));
}
return mDelayUserDataLocking;
}
private boolean allowBiometricUnlockForPrivateProfile() {
return android.os.Flags.allowPrivateProfile()
&& android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace();
}
/**
* Determines the list of users that should be stopped together with the specified
* {@code userId}. The returned list includes {@code userId}.
*/
@GuardedBy("mLock")
private @NonNull int[] getUsersToStopLU(@UserIdInt int userId) {
int startedUsersSize = mStartedUsers.size();
IntArray userIds = new IntArray();
userIds.add(userId);
int userGroupId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID);
for (int i = 0; i < startedUsersSize; i++) {
UserState uss = mStartedUsers.valueAt(i);
int startedUserId = uss.mHandle.getIdentifier();
// Skip unrelated users (profileGroupId mismatch)
int startedUserGroupId = mUserProfileGroupIds.get(startedUserId,
UserInfo.NO_PROFILE_GROUP_ID);
boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
&& (userGroupId == startedUserGroupId);
// userId has already been added
boolean sameUserId = startedUserId == userId;
if (!sameGroup || sameUserId) {
continue;
}
userIds.add(startedUserId);
}
return userIds.toArray();
}
private void forceStopUser(@UserIdInt int userId, String reason) {
if (DEBUG_MU) Slogf.i(TAG, "forceStopUser(%d): %s", userId, reason);
mInjector.activityManagerForceStopPackage(userId, reason);
if (mInjector.getUserManager().isPreCreated(userId)) {
// Don't fire intent for precreated.
return;
}
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
// Send PROFILE_INACCESSIBLE broadcast if a profile was stopped
final UserInfo userInfo = getUserInfo(userId);
if (userInfo.isProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
broadcastProfileAccessibleStateChanged(userId, parent.id,
Intent.ACTION_PROFILE_INACCESSIBLE);
//TODO(b/175704931): send ACTION_MANAGED_PROFILE_UNAVAILABLE
}
}
}
/**
* Stops the guest or ephemeral user if it has gone to the background.
*/
private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
if (DEBUG_MU) Slogf.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
synchronized(mLock) {
UserState oldUss = mStartedUsers.get(oldUserId);
if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null
|| oldUss.state == UserState.STATE_STOPPING
|| oldUss.state == UserState.STATE_SHUTDOWN) {
return;
}
}
UserInfo userInfo = getUserInfo(oldUserId);
if (userInfo.isEphemeral()) {
LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId);
}
if (userInfo.isGuest() || userInfo.isEphemeral()) {
// This is a user to be stopped.
synchronized (mLock) {
stopUsersLU(oldUserId, /* force= */ true, /* allowDelayedLocking= */ false,
null, null);
}
}
}
void scheduleStartProfiles() {
// Parent user transition to RUNNING_UNLOCKING happens on FgThread, so it is busy, there is
// a chance the profile will reach RUNNING_LOCKED while parent is still locked, so no
// attempt will be made to unlock the profile. If we go via FgThread, this will be executed
// after the parent had chance to unlock fully.
FgThread.getHandler().post(() -> {
if (!mHandler.hasMessages(START_PROFILES_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
DateUtils.SECOND_IN_MILLIS);
}
});
}
private void startProfiles() {
int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
currentUserId, false /* enabledOnly */);
List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
for (UserInfo user : profiles) {
if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED
&& user.id != currentUserId
&& shouldStartWithParent(user)) {
profilesToStart.add(user);
}
}
final int profilesToStartSize = profilesToStart.size();
int i = 0;
for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
// NOTE: this method is setting the profiles of the current user - which is always
// assigned to the default display
startUser(profilesToStart.get(i).id, USER_START_MODE_BACKGROUND_VISIBLE);
}
if (i < profilesToStartSize) {
Slogf.w(TAG, "More profiles than MAX_RUNNING_USERS");
}
}
private boolean shouldStartWithParent(UserInfo user) {
final UserProperties properties = getUserProperties(user.id);
return (properties != null && properties.getStartWithParent())
&& !user.isQuietModeEnabled();
}
/**
* Starts a {@link UserManager#isProfile() profile user}.
*
* <p>To be called from {@link com.android.server.am.ActivityManagerService}.
*
* @param userId the id of the profile user to start.
* @param evenWhenDisabled whether the profile should be started if it's not enabled yet
* (most callers should pass {@code false}, except when starting the profile while it's
* being provisioned).
* @param unlockListener listener to be informed when the profile has started and unlocked.
*
* @return {@code true} if the operation was successful.
*
* @throws IllegalArgumentException if the user doesn't exist or is not a profile.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
boolean startProfile(@UserIdInt int userId, boolean evenWhenDisabled,
@Nullable IProgressListener unlockListener) {
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 "
+ "start a profile");
}
final UserInfo userInfo = getUserInfo(userId);
if (userInfo == null || !userInfo.isProfile()) {
throw new IllegalArgumentException("User " + userId + " is not a profile");
}
if (!userInfo.isEnabled() && !evenWhenDisabled) {
Slogf.w(TAG, "Cannot start disabled profile #%d", userId);
return false;
}
return startUserNoChecks(userId, Display.DEFAULT_DISPLAY,
USER_START_MODE_BACKGROUND_VISIBLE, unlockListener);
}
@VisibleForTesting
boolean startUser(@UserIdInt int userId, @UserStartMode int userStartMode) {
return startUser(userId, userStartMode, /* unlockListener= */ null);
}
/**
* Start user, if its not already running.
*
* <p>The user will be brought to the foreground, if {@code userStartMode} parameter is
* set to {@link UserManagerInternal#USER_START_MODE_FOREGROUND}
* When starting the user, multiple intents will be broadcast in the following order:</p>
* <ul>
* <li>{@link Intent#ACTION_USER_STARTED} - sent to registered receivers of the new user
* <li>{@link Intent#ACTION_USER_BACKGROUND} - sent to registered receivers of the outgoing
* user and all profiles of this user. Sent only if {@code foreground} parameter is
* {@code false}
* <li>{@link Intent#ACTION_USER_FOREGROUND} - sent to registered receivers of the new
* user and all profiles of this user. Sent only if {@code foreground} parameter is
* {@code true}
* <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user.
* Sent only if {@code foreground} parameter is {@code true}
* <li>{@link Intent#ACTION_USER_STARTING} - ordered broadcast sent to registered receivers
* of the new fg user
* <li>{@link Intent#ACTION_LOCKED_BOOT_COMPLETED} - ordered broadcast sent to receivers of
* the new user
* <li>{@link Intent#ACTION_USER_UNLOCKED} - sent to registered receivers of the new user
* <li>{@link Intent#ACTION_PRE_BOOT_COMPLETED} - ordered broadcast sent to receivers of the
* new user. Sent only when the user is booting after a system update.
* <li>{@link Intent#ACTION_USER_INITIALIZE} - ordered broadcast sent to receivers of the
* new user. Sent only the first time a user is starting.
* <li>{@link Intent#ACTION_BOOT_COMPLETED} - ordered broadcast sent to receivers of the new
* user. Indicates that the user has finished booting.
* </ul>
*
* @param userId ID of the user to start
* @param userStartMode user starting mode
* @param unlockListener Listener to be informed when the user has started and unlocked.
* @return true if the user has been successfully started
*/
boolean startUser(@UserIdInt int userId, @UserStartMode int userStartMode,
@Nullable IProgressListener unlockListener) {
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser");
return startUserNoChecks(userId, Display.DEFAULT_DISPLAY, userStartMode, unlockListener);
}
/**
* Starts a user in background and make it visible in the given display.
*
* <p>This call will trigger the usual "user started" lifecycle events (i.e., `SystemService`
* callbacks and app intents), plus a call to
* {@link UserManagerInternal.UserVisibilityListener#onUserVisibilityChanged(int, boolean)} if
* the user visibility changed. Notice that the visibility change is independent of the user
* workflow state, and they can mismatch in some corner events (for example, if the user was
* already running in the background but not associated with a display, this call for that user
* would not trigger any lifecycle event but would trigger {@code onUserVisibilityChanged}).
*
* <p>See {@link ActivityManager#startUserInBackgroundOnSecondaryDisplay(int, int)} for more
* semantics.
*
* @param userId user to be started
* @param displayId display where the user will be visible
* @param unlockListener Listener to be informed when the user has started and unlocked.
*
* @return whether the user was started
*/
boolean startUserVisibleOnDisplay(@UserIdInt int userId, int displayId,
@Nullable IProgressListener unlockListener) {
checkCallingHasOneOfThosePermissions("startUserOnDisplay",
MANAGE_USERS, INTERACT_ACROSS_USERS);
try {
return startUserNoChecks(userId, displayId, USER_START_MODE_BACKGROUND_VISIBLE,
unlockListener);
} catch (RuntimeException e) {
Slogf.e(TAG, "startUserOnSecondaryDisplay(%d, %d) failed: %s", userId, displayId, e);
return false;
}
}
private boolean startUserNoChecks(@UserIdInt int userId, int displayId,
@UserStartMode int userStartMode, @Nullable IProgressListener unlockListener) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("UserController.startUser-" + userId
+ (displayId == Display.DEFAULT_DISPLAY ? "" : "-display-" + displayId)
+ "-" + (userStartMode == USER_START_MODE_FOREGROUND ? "fg" : "bg")
+ "-start-mode-" + userStartMode);
try {
return startUserInternal(userId, displayId, userStartMode, unlockListener, t);
} finally {
t.traceEnd();
}
}
private boolean startUserInternal(@UserIdInt int userId, int displayId,
@UserStartMode int userStartMode, @Nullable IProgressListener unlockListener,
TimingsTraceAndSlog t) {
if (DEBUG_MU) {
Slogf.i(TAG, "Starting user %d on display %d with mode %s", userId, displayId,
userStartModeToString(userStartMode));
}
boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
boolean onSecondaryDisplay = displayId != Display.DEFAULT_DISPLAY;
if (onSecondaryDisplay) {
Preconditions.checkArgument(!foreground, "Cannot start user %d in foreground AND "
+ "on secondary display (%d)", userId, displayId);
}
EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId, foreground ? 1 : 0,
displayId);
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
t.traceBegin("getStartedUserState");
final int oldUserId = getCurrentUserId();
if (oldUserId == userId) {
final UserState state = getStartedUserState(userId);
if (state == null) {
Slogf.wtf(TAG, "Current user has no UserState");
// continue starting.
} else {
if (userId == UserHandle.USER_SYSTEM && state.state == STATE_BOOTING) {
// system user start explicitly requested. should continue starting as it
// is not in running state.
} else {
if (state.state == STATE_RUNNING_UNLOCKED) {
// We'll skip all later code, so we must tell listener it's already
// unlocked.
notifyFinished(userId, unlockListener);
}
t.traceEnd(); //getStartedUserState
return true;
}
}
}
t.traceEnd(); //getStartedUserState
if (foreground) {
t.traceBegin("clearAllLockedTasks");
mInjector.clearAllLockedTasks("startUser");
t.traceEnd();
}
t.traceBegin("getUserInfo");
final UserInfo userInfo = getUserInfo(userId);
t.traceEnd();
if (userInfo == null) {
Slogf.w(TAG, "No user info for user #" + userId);
return false;
}
if (foreground && userInfo.isProfile()) {
Slogf.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
return false;
}
if ((foreground || onSecondaryDisplay) && userInfo.preCreated) {
Slogf.w(TAG, "Cannot start pre-created user #" + userId + " in foreground or on "
+ "secondary display");
return false;
}
t.traceBegin("assignUserToDisplayOnStart");
int result = mInjector.getUserManagerInternal().assignUserToDisplayOnStart(userId,
userInfo.profileGroupId, userStartMode, displayId);
t.traceEnd();
if (result == USER_ASSIGNMENT_RESULT_FAILURE) {
Slogf.e(TAG, "%s user(%d) / display (%d) assignment failed: %s",
userStartModeToString(userStartMode), userId, displayId,
userAssignmentResultToString(result));
return false;
}
boolean needStart = false;
boolean updateUmState = false;
UserState uss;
// If the user we are switching to is not currently started, then
// we need to start it now.
t.traceBegin("updateStartedUserArrayStarting");
synchronized (mLock) {
uss = mStartedUsers.get(userId);
if (uss == null) {
uss = new UserState(UserHandle.of(userId));
uss.mUnlockProgress.addListener(new UserProgressListener());
mStartedUsers.put(userId, uss);
updateStartedUserArrayLU();
needStart = true;
updateUmState = true;
} else if (uss.state == UserState.STATE_SHUTDOWN) {
Slogf.i(TAG, "User #" + userId
+ " is shutting down - will start after full shutdown");
mPendingUserStarts.add(new PendingUserStart(userId, userStartMode,
unlockListener));
t.traceEnd(); // updateStartedUserArrayStarting
return true;
}
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
}
if (unlockListener != null) {
uss.mUnlockProgress.addListener(unlockListener);
}
t.traceEnd(); // updateStartedUserArrayStarting
if (updateUmState) {
t.traceBegin("setUserState");
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
t.traceEnd();
}
t.traceBegin("updateConfigurationAndProfileIds");
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
mInjector.reportGlobalUsageEvent(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
boolean userSwitchUiEnabled;
synchronized (mLock) {
mCurrentUserId = userId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
mInjector.updateUserConfiguration();
// NOTE: updateProfileRelatedCaches() is called on both if and else parts, ideally
// it should be moved outside, but for now it's not as there are many calls to
// external components here afterwards
updateProfileRelatedCaches();
mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
if (userSwitchUiEnabled) {
mInjector.getWindowManager().setSwitchingUser(true);
// Only lock if the user has a secure keyguard PIN/Pattern/Pwd
if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
// Make sure the device is locked before moving on with the user switch
mInjector.lockDeviceNowAndWaitForKeyguardShown();
}
}
} else {
final Integer currentUserIdInt = mCurrentUserId;
updateProfileRelatedCaches();
synchronized (mLock) {
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
}
t.traceEnd();
// 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) {
t.traceBegin("updateStateStopping");
// If we are stopping, we haven't sent ACTION_SHUTDOWN,
// so we can just fairly silently bring the user back from
// the almost-dead.
uss.setState(uss.lastState);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
synchronized (mLock) {
updateStartedUserArrayLU();
}
needStart = true;
t.traceEnd();
} else if (uss.state == UserState.STATE_SHUTDOWN) {
t.traceBegin("updateStateShutdown");
// This means ACTION_SHUTDOWN has been sent, so we will
// need to treat this as a new boot of the user.
uss.setState(UserState.STATE_BOOTING);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
synchronized (mLock) {
updateStartedUserArrayLU();
}
needStart = true;
t.traceEnd();
}
if (uss.state == UserState.STATE_BOOTING) {
t.traceBegin("updateStateBooting");
// Give user manager a chance to propagate user restrictions
// to other services and prepare app storage
mInjector.getUserManager().onBeforeStartUser(userId);
// 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, NO_ARG2));
t.traceEnd();
}
t.traceBegin("sendMessages");
if (foreground) {
mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
oldUserId, userId, uss));
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
oldUserId, userId, uss), getUserSwitchTimeoutMs());
}
if (userInfo.preCreated) {
needStart = false;
}
// In most cases, broadcast for the system user starting/started is sent by
// ActivityManagerService#systemReady(). However on some HSUM devices (e.g. tablets)
// the user switches from the system user to a secondary user while running
// ActivityManagerService#systemReady(), thus broadcast is not sent for the system user.
// Therefore we send the broadcast for the system user here as well in HSUM.
// TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
// in HSUM. Ideally it'd be best to have one single place that sends this notification.
final boolean isSystemUserInHeadlessMode = (userId == UserHandle.USER_SYSTEM)
&& mInjector.isHeadlessSystemUserMode();
if (needStart || isSystemUserInHeadlessMode) {
sendUserStartedBroadcast(userId, callingUid, callingPid);
}
t.traceEnd();
if (foreground) {
t.traceBegin("moveUserToForeground");
moveUserToForeground(uss, userId);
t.traceEnd();
} else {
t.traceBegin("finishUserBoot");
finishUserBoot(uss);
t.traceEnd();
}
if (needStart || isSystemUserInHeadlessMode) {
t.traceBegin("sendRestartBroadcast");
sendUserStartingBroadcast(userId, callingUid, callingPid);
t.traceEnd();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
/**
* Start user, if its not already running, and bring it to foreground.
*/
void startUserInForeground(@UserIdInt int targetUserId) {
boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND);
if (!success) {
mInjector.getWindowManager().setSwitchingUser(false);
dismissUserSwitchDialog(this::endUserSwitch);
}
}
boolean unlockUser(@UserIdInt int userId, @Nullable IProgressListener listener) {
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "unlockUser");
EventLog.writeEvent(EventLogTags.UC_UNLOCK_USER, userId);
final long binderToken = Binder.clearCallingIdentity();
try {
return maybeUnlockUser(userId, listener);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
private static void notifyFinished(@UserIdInt int userId,
@Nullable IProgressListener listener) {
if (listener == null) return;
try {
listener.onFinished(userId, null);
} catch (RemoteException ignored) {
}
}
private boolean maybeUnlockUser(@UserIdInt int userId) {
return maybeUnlockUser(userId, null);
}
/**
* Tries to unlock the given user.
* <p>
* This will succeed only if the user's CE storage key is already unlocked or if the user
* doesn't have a lockscreen credential set.
*/
private boolean maybeUnlockUser(@UserIdInt int userId, @Nullable IProgressListener listener) {
// We cannot allow users to be unlocked before PHASE_BOOT_COMPLETED, for two reasons.
// First, emulated volumes aren't supposed to be used until then; StorageManagerService
// assumes it can reset everything upon reaching PHASE_BOOT_COMPLETED. Second, on some
// devices the Weaver HAL needed to unlock the user's storage isn't available until sometime
// shortly before PHASE_BOOT_COMPLETED. The below logic enforces a consistent flow across
// all devices, regardless of their Weaver implementation.
//
// Any unlocks that get delayed by this will be done by onBootComplete() instead.
if (!mAllowUserUnlocking) {
Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId);
notifyFinished(userId, listener);
return false;
}
UserState uss;
if (!StorageManager.isCeStorageUnlocked(userId)) {
// We always want to try to unlock CE storage, even if the user is not started yet.
mLockPatternUtils.unlockUserKeyIfUnsecured(userId);
}
synchronized (mLock) {
// Register the given listener to watch for unlock progress
uss = mStartedUsers.get(userId);
if (uss != null) {
uss.mUnlockProgress.addListener(listener);
}
}
// Bail if user isn't actually running
if (uss == null) {
notifyFinished(userId, listener);
return false;
}
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("finishUserUnlocking-" + userId);
final boolean finishUserUnlockingResult = finishUserUnlocking(uss);
t.traceEnd();
if (!finishUserUnlockingResult) {
notifyFinished(userId, listener);
return false;
}
// We just unlocked a user, so let's now attempt to unlock any profiles under that user.
// First, get list of userIds. Requires mLock, so we cannot make external calls, e.g. to UMS
int[] userIds;
synchronized (mLock) {
userIds = new int[mStartedUsers.size()];
for (int i = 0; i < userIds.length; i++) {
userIds[i] = mStartedUsers.keyAt(i);
}
}
for (int testUserId : userIds) {
final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
if (parent != null && parent.id == userId && testUserId != userId) {
Slogf.d(TAG, "User " + testUserId + " (parent " + parent.id
+ "): attempting unlock because parent was just unlocked");
maybeUnlockUser(testUserId);
}
}
return true;
}
boolean switchUser(final int targetUserId) {
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId);
EventLog.writeEvent(EventLogTags.UC_SWITCH_USER, targetUserId);
int currentUserId = getCurrentUserId();
UserInfo targetUserInfo = getUserInfo(targetUserId);
boolean userSwitchUiEnabled;
synchronized (mLock) {
if (targetUserId == currentUserId && mTargetUserId == UserHandle.USER_NULL) {
Slogf.i(TAG, "user #" + targetUserId + " is already the current user");
return true;
}
if (targetUserInfo == null) {
Slogf.w(TAG, "No user info for user #" + targetUserId);
return false;
}
if (!targetUserInfo.supportsSwitchTo()) {
Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
return false;
}
if (FactoryResetter.isFactoryResetting()) {
Slogf.w(TAG, "Cannot switch to User #" + targetUserId
+ ": factory reset in progress");
return false;
}
if (!mInitialized) {
Slogf.e(TAG, "Cannot switch to User #" + targetUserId
+ ": UserController not ready yet");
return false;
}
if (mTargetUserId != UserHandle.USER_NULL) {
Slogf.w(TAG, "There is already an ongoing user switch to User #" + mTargetUserId
+ ". User #" + targetUserId + " will be added to the queue.");
mPendingTargetUserIds.offer(targetUserId);
return true;
}
mTargetUserId = targetUserId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
mInjector.setHasTopUi(true);
}
if (userSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
mUiHandler.sendMessage(mUiHandler.obtainMessage(
START_USER_SWITCH_UI_MSG, userNames));
} else {
sendStartUserSwitchFgMessage(targetUserId);
}
return true;
}
private void sendStartUserSwitchFgMessage(int targetUserId) {
mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_FG_MSG, targetUserId, 0));
}
private void dismissUserSwitchDialog(Runnable onDismissed) {
mUiHandler.post(() -> mInjector.dismissUserSwitchingDialog(onDismissed));
}
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
getSwitchingFromSystemUserMessageUnchecked(),
getSwitchingToSystemUserMessageUnchecked(),
/* onShown= */ () -> sendStartUserSwitchFgMessage(fromToUserPair.second.id));
}
private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
mUserSwitchObservers.getBroadcastItem(i).onForegroundProfileSwitch(userId);
} catch (RemoteException e) {
// Ignore
}
}
mUserSwitchObservers.finishBroadcast();
}
/** Called on handler thread */
@VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("dispatchUserSwitchComplete-" + newUserId);
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
t.traceBegin("onUserSwitchComplete-" + newUserId + " #" + i + " "
+ mUserSwitchObservers.getBroadcastCookie(i));
mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
t.traceEnd();
} catch (RemoteException e) {
// Ignore
}
}
mUserSwitchObservers.finishBroadcast();
t.traceBegin("sendUserSwitchBroadcasts-" + oldUserId + "-" + newUserId);
sendUserSwitchBroadcasts(oldUserId, newUserId);
t.traceEnd();
t.traceEnd();
endUserSwitch();
}
private void endUserSwitch() {
if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
mInjector.setHasTopUi(false);
}
final int nextUserId;
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
mTargetUserId = UserHandle.USER_NULL;
}
if (nextUserId != UserHandle.USER_NULL) {
switchUser(nextUserId);
}
}
private void dispatchLockedBootComplete(@UserIdInt int userId) {
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
mUserSwitchObservers.getBroadcastItem(i).onLockedBootComplete(userId);
} catch (RemoteException e) {
// Ignore
}
}
mUserSwitchObservers.finishBroadcast();
}
private void stopUserOnSwitchIfEnforced(@UserIdInt int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
return;
}
boolean hasRestriction =
hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
synchronized (mLock) {
// If running in background is disabled or mStopUserOnSwitch mode, stop the user.
boolean disallowRunInBg = hasRestriction || shouldStopUserOnSwitch();
if (!disallowRunInBg) {
if (DEBUG_MU) {
Slogf.i(TAG, "stopUserOnSwitchIfEnforced() NOT stopping %d and related users",
oldUserId);
}
return;
}
if (DEBUG_MU) {
Slogf.i(TAG, "stopUserOnSwitchIfEnforced() stopping %d and related users",
oldUserId);
}
stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
null, null);
}
}
private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("timeoutUserSwitch-" + oldUserId + "-to-" + newUserId);
synchronized (mLock) {
Slogf.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG);
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
// Report observers that never called back (USER_SWITCH_CALLBACKS_TIMEOUT)
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG,
oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS);
}
t.traceEnd();
}
private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
synchronized (mLock) {
if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
Slogf.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+ ". Observers that didn't respond: " + mTimeoutUserSwitchCallbacks);
mTimeoutUserSwitchCallbacks = null;
}
}
}
@VisibleForTesting
void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
if (observerCount > 0) {
for (int i = 0; i < observerCount; i++) {
final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
t.traceBegin("onBeforeUserSwitching-" + name);
try {
mUserSwitchObservers.getBroadcastItem(i).onBeforeUserSwitching(newUserId);
} catch (RemoteException e) {
// Ignore
} finally {
t.traceEnd();
}
}
final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
synchronized (mLock) {
uss.switching = true;
mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
}
final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
final long dispatchStartedTime = SystemClock.elapsedRealtime();
for (int i = 0; i < observerCount; i++) {
final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
try {
// Prepend with unique prefix to guarantee that keys are unique
final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
synchronized (mLock) {
curWaitingUserSwitchCallbacks.add(name);
}
final IRemoteCallback callback = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
asyncTraceEnd("onUserSwitching-" + name, newUserId);
synchronized (mLock) {
long delayForObserver = SystemClock.elapsedRealtime()
- dispatchStartedTimeForObserver;
if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
Slogf.w(TAG, "User switch slowed down by observer " + name
+ ": result took " + delayForObserver
+ " ms to process.");
}
long totalDelay = SystemClock.elapsedRealtime()
- dispatchStartedTime;
if (totalDelay > userSwitchTimeoutMs) {
Slogf.e(TAG, "User switch timeout: observer " + name
+ "'s result was received " + totalDelay
+ " ms after dispatchUserSwitch.");
}
curWaitingUserSwitchCallbacks.remove(name);
// Continue switching if all callbacks have been notified and
// user switching session is still valid
if (waitingCallbacksCount.decrementAndGet() == 0
&& (curWaitingUserSwitchCallbacks
== mCurWaitingUserSwitchCallbacks)) {
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
}
};
asyncTraceBegin("onUserSwitching-" + name, newUserId);
mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
} catch (RemoteException e) {
// Ignore
}
}
} else {
synchronized (mLock) {
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
mUserSwitchObservers.finishBroadcast();
t.traceEnd(); // end dispatchUserSwitch-
}
@GuardedBy("mLock")
private void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("sendContinueUserSwitchLU-" + oldUserId + "-to-" + newUserId);
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
t.traceEnd();
}
@VisibleForTesting
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("continueUserSwitch-" + oldUserId + "-to-" + newUserId);
EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
// Do the keyguard dismiss and dismiss the user switching dialog later
mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId));
uss.switching = false;
stopGuestOrEphemeralUserIfBackground(oldUserId);
stopUserOnSwitchIfEnforced(oldUserId);
t.traceEnd(); // end continueUserSwitch
}
@VisibleForTesting
void completeUserSwitch(int oldUserId, int newUserId) {
final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
// serialize each conditional step
await(
// STEP 1 - If there is no challenge set, dismiss the keyguard right away
isUserSwitchUiEnabled && !mInjector.getKeyguardManager().isDeviceSecure(newUserId),
mInjector::dismissKeyguard,
() -> await(
// STEP 2 - If user switch ui was enabled, dismiss user switch dialog
isUserSwitchUiEnabled,
this::dismissUserSwitchDialog,
() -> {
// STEP 3 - Send REPORT_USER_SWITCH_COMPLETE_MSG to broadcast
// ACTION_USER_SWITCHED & call UserSwitchObservers.onUserSwitchComplete
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
}
));
}
private void await(boolean condition, Consumer<Runnable> conditionalStep, Runnable nextStep) {
if (condition) {
conditionalStep.accept(nextStep);
} else {
nextStep.run();
}
}
private void moveUserToForeground(UserState uss, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivity(newUserId, "moveUserToForeground");
} else {
mInjector.taskSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
}
// The two methods sendUserStartedBroadcast() and sendUserStartingBroadcast()
// could be merged for better reuse. However, the params they are calling broadcastIntent()
// with are different - resultCode receiver, permissions, ordered, and userId, etc. Therefore,
// we decided to keep two separate methods for better code readability/clarity.
// TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
// in HSUM. Ideally it'd be best to have one single place that sends this notification.
/** Sends {@code ACTION_USER_STARTED} broadcast. */
void sendUserStartedBroadcast(@UserIdInt int userId, int callingUid, int callingPid) {
if (userId == UserHandle.USER_SYSTEM) {
synchronized (mLock) {
// Make sure that the broadcast is sent only once for the system user.
if (mIsBroadcastSentForSystemUserStarted) {
return;
}
mIsBroadcastSentForSystemUserStarted = true;
}
}
final Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */ null,
/* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null,
/* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */ null,
/* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID,
callingUid, callingPid, userId);
}
/** Sends {@code ACTION_USER_STARTING} broadcast. */
void sendUserStartingBroadcast(@UserIdInt int userId, int callingUid, int callingPid) {
if (userId == UserHandle.USER_SYSTEM) {
synchronized (mLock) {
// Make sure that the broadcast is sent only once for the system user.
if (mIsBroadcastSentForSystemUserStarting) {
return;
}
mIsBroadcastSentForSystemUserStarting = true;
}
}
final Intent intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mInjector.broadcastIntent(intent, /* resolvedType= */ null,
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky,
int sendingUser) throws RemoteException {
}
}, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null,
new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, /* bOptions= */ null,
/* ordered= */ true, /* sticky= */ false, MY_PID, SYSTEM_UID,
callingUid, callingPid, UserHandle.USER_ALL);
}
void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
Intent intent;
if (oldUserId >= 0) {
// Send USER_BACKGROUND broadcast to all profiles of the outgoing user
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(oldUserId, false);
int count = profiles.size();
for (int i = 0; i < count; i++) {
int profileUserId = profiles.get(i).id;
intent = new Intent(Intent.ACTION_USER_BACKGROUND);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
profileUserId);
}
}
if (newUserId >= 0) {
// Send USER_FOREGROUND broadcast to all profiles of the incoming user
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(newUserId, false);
int count = profiles.size();
for (int i = 0; i < count; i++) {
int profileUserId = profiles.get(i).id;
intent = new Intent(Intent.ACTION_USER_FOREGROUND);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
profileUserId);
}
intent = new Intent(Intent.ACTION_USER_SWITCHED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(newUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.MANAGE_USERS},
AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid,
callingPid, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Broadcasts to the parent user when a profile is started+unlocked/stopped.
* @param userId the id of the profile
* @param parentId the id of the parent user
* @param intentAction either ACTION_PROFILE_ACCESSIBLE or ACTION_PROFILE_INACCESSIBLE
*/
private void broadcastProfileAccessibleStateChanged(@UserIdInt int userId,
@UserIdInt int parentId,
String intentAction) {
final Intent intent = new Intent(intentAction);
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */
null, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */
null, /* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */
null, /* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(), parentId);
}
int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId, boolean allowAll,
int allowMode, String name, String callerPackage) {
final int callingUserId = UserHandle.getUserId(callingUid);
if (callingUserId == userId) {
return userId;
}
// Note that we may be accessing mCurrentUserId outside of a lock...
// shouldn't be a big deal, if this is being called outside
// of a locked context there is intrinsically a race with
// the value the caller will receive and someone else changing it.
// We assume that USER_CURRENT_OR_SELF will use the current user; later
// we will switch to the calling user if access to the current user fails.
int targetUserId = unsafeConvertIncomingUser(userId);
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
if (mInjector.isCallerRecents(callingUid) && isSameProfileGroup) {
// If the caller is Recents and the caller has ownership of the profile group,
// we then allow it to access its profiles.
allow = true;
} else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
// If the caller has this permission, they always pass go. And collect $200.
allow = true;
} else if (allowMode == ALLOW_FULL_ONLY) {
// We require full access, sucks to be you.
allow = false;
} else if (canInteractWithAcrossProfilesPermission(
allowMode, isSameProfileGroup, callingPid, callingUid, callerPackage)) {
allow = true;
} else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
allow = false;
} else if (allowMode == ALLOW_NON_FULL || allowMode == ALLOW_PROFILES_OR_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
} else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
allow = isSameProfileGroup;
} else {
throw new IllegalArgumentException("Unknown mode: " + allowMode);
}
if (!allow) {
if (userId == UserHandle.USER_CURRENT_OR_SELF) {
// In this case, they would like to just execute as their
// owner user instead of failing.
targetUserId = callingUserId;
} else {
StringBuilder builder = new StringBuilder(128);
builder.append("Permission Denial: ");
builder.append(name);
if (callerPackage != null) {
builder.append(" from ");
builder.append(callerPackage);
}
builder.append(" asks to run as user ");
builder.append(userId);
builder.append(" but is calling from uid ");
UserHandle.formatUid(builder, callingUid);
builder.append("; this requires ");
builder.append(INTERACT_ACROSS_USERS_FULL);
if (allowMode != ALLOW_FULL_ONLY) {
if (allowMode == ALLOW_NON_FULL
|| allowMode == ALLOW_PROFILES_OR_NON_FULL
|| (allowMode == ALLOW_NON_FULL_IN_PROFILE && isSameProfileGroup)) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
}
if (isSameProfileGroup && allowMode == ALLOW_PROFILES_OR_NON_FULL) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_PROFILES);
}
}
String msg = builder.toString();
Slogf.w(TAG, msg);
throw new SecurityException(msg);
}
}
}
if (!allowAll) {
ensureNotSpecialUser(targetUserId);
}
// Check shell permission
if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) {
if (hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId)) {
throw new SecurityException("Shell does not have permission to access user "
+ targetUserId + "\n " + Debug.getCallers(3));
}
}
return targetUserId;
}
private boolean canInteractWithAcrossProfilesPermission(
int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
String callingPackage) {
if (allowMode != ALLOW_PROFILES_OR_NON_FULL) {
return false;
}
if (!isSameProfileGroup) {
return false;
}
return mInjector.checkPermissionForPreflight(INTERACT_ACROSS_PROFILES, callingPid,
callingUid, callingPackage);
}
int unsafeConvertIncomingUser(@UserIdInt int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
? getCurrentUserId(): userId;
}
void ensureNotSpecialUser(@UserIdInt int userId) {
if (userId >= 0) {
return;
}
throw new IllegalArgumentException("Call does not support special user #" + userId);
}
void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
Objects.requireNonNull(name, "Observer name cannot be null");
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "registerUserSwitchObserver");
mUserSwitchObservers.register(observer, name);
}
void sendForegroundProfileChanged(@UserIdInt int userId) {
mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, userId, 0).sendToTarget();
}
void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
mUserSwitchObservers.unregister(observer);
}
UserState getStartedUserState(@UserIdInt int userId) {
synchronized (mLock) {
return mStartedUsers.get(userId);
}
}
boolean hasStartedUserState(@UserIdInt int userId) {
synchronized (mLock) {
return mStartedUsers.get(userId) != null;
}
}
@GuardedBy("mLock")
private void updateStartedUserArrayLU() {
int num = 0;
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
// This list does not include stopping users.
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
num++;
}
}
mStartedUserArray = new int[num];
num = 0;
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
mStartedUserArray[num++] = mStartedUsers.keyAt(i);
}
}
}
@VisibleForTesting
void setAllowUserUnlocking(boolean allowed) {
mAllowUserUnlocking = allowed;
if (DEBUG_MU) {
Slogf.d(TAG, new Exception(), "setAllowUserUnlocking(%b)", allowed);
}
}
void onBootComplete(IIntentReceiver resultTo) {
// Now that PHASE_BOOT_COMPLETED has been reached, user unlocking is allowed.
setAllowUserUnlocking(true);
// Get a copy of mStartedUsers to use outside of lock.
SparseArray<UserState> startedUsers;
synchronized (mLock) {
startedUsers = mStartedUsers.clone();
}
// In non-headless system user mode, call finishUserBoot() to transition the system user
// from the BOOTING state to RUNNING_LOCKED, then to RUNNING_UNLOCKED if possible.
//
// In headless system user mode, additional users may have been started, and all users
// (including the system user) that ever get started are started explicitly. In this case,
// we should *not* transition users out of the BOOTING state using finishUserBoot(), as that
// doesn't handle issuing the needed onUserStarting() call, and it would just race with an
// explicit start anyway. We do, however, need to send the "locked boot complete" broadcast
// as that got skipped earlier due to the *device* boot not being complete yet.
// We also need to try to unlock all started users, since until now explicit user starts
// didn't proceed to unlocking, due to it being too early in the device boot.
//
// USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest.
Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM);
for (int i = 0; i < startedUsers.size(); i++) {
int userId = startedUsers.keyAt(i);
UserState uss = startedUsers.valueAt(i);
if (!mInjector.isHeadlessSystemUserMode()) {
finishUserBoot(uss, resultTo);
} else {
sendLockedBootCompletedBroadcast(resultTo, userId);
maybeUnlockUser(userId);
}
}
}
void onSystemReady() {
if (DEBUG_MU) {
Slogf.d(TAG, "onSystemReady()");
}
mInjector.getUserManagerInternal().addUserLifecycleListener(mUserLifecycleListener);
updateProfileRelatedCaches();
mInjector.reportCurWakefulnessUsageEvent();
}
// TODO(b/266158156): remove this method if initial system user boot logic is refactored?
void onSystemUserStarting() {
if (!mInjector.isHeadlessSystemUserMode()) {
// Don't need to call on HSUM because it will be called when the system user is
// restarted on background
mInjector.onUserStarting(UserHandle.USER_SYSTEM);
mInjector.onSystemUserVisibilityChanged(/* visible= */ true);
}
}
/**
* Refreshes the internal caches related to user profiles.
*
* <p>It's called every time a user is started.
*/
private void updateProfileRelatedCaches() {
final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(),
/* enabledOnly= */ false);
int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
for (int i = 0; i < currentProfileIds.length; i++) {
currentProfileIds[i] = profiles.get(i).id;
}
final List<UserInfo> users = mInjector.getUserManager().getUsers(false);
synchronized (mLock) {
mCurrentProfileIds = currentProfileIds;
mUserProfileGroupIds.clear();
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
mUserProfileGroupIds.put(user.id, user.profileGroupId);
}
}
}
}
int[] getStartedUserArray() {
synchronized (mLock) {
return mStartedUserArray;
}
}
boolean isUserRunning(@UserIdInt int userId, int flags) {
UserState state = getStartedUserState(userId);
if (state == null) {
return false;
}
if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
return true;
}
if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0) {
switch (state.state) {
case UserState.STATE_BOOTING:
case UserState.STATE_RUNNING_LOCKED:
return true;
default:
return false;
}
}
if ((flags & ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED) != 0) {
switch (state.state) {
case UserState.STATE_RUNNING_UNLOCKING:
case UserState.STATE_RUNNING_UNLOCKED:
return true;
// In the stopping/shutdown state, return unlock state of the user's CE storage.
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
return StorageManager.isCeStorageUnlocked(userId);
default:
return false;
}
}
if ((flags & ActivityManager.FLAG_AND_UNLOCKED) != 0) {
switch (state.state) {
case UserState.STATE_RUNNING_UNLOCKED:
return true;
// In the stopping/shutdown state, return unlock state of the user's CE storage.
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
return StorageManager.isCeStorageUnlocked(userId);
default:
return false;
}
}
return state.state != UserState.STATE_STOPPING && state.state != UserState.STATE_SHUTDOWN;
}
/**
* Check if system user is already started. Unlike other user, system user is in STATE_BOOTING
* even if it is not explicitly started. So isUserRunning cannot give the right state
* to check if system user is started or not.
* @return true if system user is started.
*/
boolean isSystemUserStarted() {
synchronized (mLock) {
UserState uss = mStartedUsers.get(UserHandle.USER_SYSTEM);
if (uss == null) {
return false;
}
return uss.state == UserState.STATE_RUNNING_LOCKED
|| uss.state == UserState.STATE_RUNNING_UNLOCKING
|| uss.state == UserState.STATE_RUNNING_UNLOCKED;
}
}
private void checkGetCurrentUserPermissions() {
if ((mInjector.checkCallingPermission(INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) && (
mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED)) {
String msg = "Permission Denial: getCurrentUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + INTERACT_ACROSS_USERS;
Slogf.w(TAG, msg);
throw new SecurityException(msg);
}
}
UserInfo getCurrentUser() {
checkGetCurrentUserPermissions();
// Optimization - if there is no pending user switch, return user for current id
// (no need to acquire lock because mTargetUserId and mCurrentUserId are volatile)
if (mTargetUserId == UserHandle.USER_NULL) {
return getUserInfo(mCurrentUserId);
}
synchronized (mLock) {
return getCurrentUserLU();
}
}
/**
* Gets the current user id, but checking that caller has the proper permissions.
*/
int getCurrentUserIdChecked() {
checkGetCurrentUserPermissions();
// Optimization - if there is no pending user switch, return current id
// (no need to acquire lock because mTargetUserId and mCurrentUserId are volatile)
if (mTargetUserId == UserHandle.USER_NULL) {
return mCurrentUserId;
}
return getCurrentOrTargetUserId();
}
@GuardedBy("mLock")
private UserInfo getCurrentUserLU() {
int userId = getCurrentOrTargetUserIdLU();
return getUserInfo(userId);
}
int getCurrentOrTargetUserId() {
synchronized (mLock) {
return getCurrentOrTargetUserIdLU();
}
}
@GuardedBy("mLock")
private int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
Pair<Integer, Integer> getCurrentAndTargetUserIds() {
synchronized (mLock) {
return new Pair<>(mCurrentUserId, mTargetUserId);
}
}
@GuardedBy("mLock")
private int getCurrentUserIdLU() {
return mCurrentUserId;
}
int getCurrentUserId() {
synchronized (mLock) {
return mCurrentUserId;
}
}
@GuardedBy("mLock")
private boolean isCurrentUserLU(@UserIdInt int userId) {
return userId == getCurrentOrTargetUserIdLU();
}
/** Returns whether the user is always-visible (such as a communal profile). */
private boolean isAlwaysVisibleUser(@UserIdInt int userId) {
final UserProperties properties = getUserProperties(userId);
return properties != null && properties.getAlwaysVisible();
}
int[] getUsers() {
UserManagerService ums = mInjector.getUserManager();
return ums != null ? ums.getUserIds() : new int[] { 0 };
}
private UserInfo getUserInfo(@UserIdInt int userId) {
return mInjector.getUserManager().getUserInfo(userId);
}
private @Nullable UserProperties getUserProperties(@UserIdInt int userId) {
return mInjector.getUserManagerInternal().getUserProperties(userId);
}
int[] getUserIds() {
return mInjector.getUserManager().getUserIds();
}
/**
* If {@code userId} is {@link UserHandle#USER_ALL}, then return an array with all running user
* IDs. Otherwise return an array whose only element is the given user id.
*
* It doesn't handle other special user IDs such as {@link UserHandle#USER_CURRENT}.
*/
int[] expandUserId(@UserIdInt int userId) {
if (userId != UserHandle.USER_ALL) {
return new int[] {userId};
} else {
return getUsers();
}
}
boolean exists(@UserIdInt int userId) {
return mInjector.getUserManager().exists(userId);
}
private void checkCallingPermission(String permission, String methodName) {
checkCallingHasOneOfThosePermissions(methodName, permission);
}
private void checkCallingHasOneOfThosePermissions(String methodName, String...permissions) {
for (String permission : permissions) {
if (mInjector.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
return;
}
}
String msg = "Permission denial: " + methodName
+ "() from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires "
+ (permissions.length == 1
? permissions[0]
: "one of " + Arrays.toString(permissions));
Slogf.w(TAG, msg);
throw new SecurityException(msg);
}
private void enforceShellRestriction(String restriction, @UserIdInt int userId) {
if (Binder.getCallingUid() == SHELL_UID) {
if (userId < 0 || hasUserRestriction(restriction, userId)) {
throw new SecurityException("Shell does not have permission to access user "
+ userId);
}
}
}
boolean hasUserRestriction(String restriction, @UserIdInt int userId) {
return mInjector.getUserManager().hasUserRestriction(restriction, userId);
}
/** Returns whether the two users are in the same profile group. */
boolean isSameProfileGroup(int callingUserId, int targetUserId) {
if (callingUserId == targetUserId) {
return true;
}
synchronized (mLock) {
int callingProfile = mUserProfileGroupIds.get(callingUserId,
UserInfo.NO_PROFILE_GROUP_ID);
int targetProfile = mUserProfileGroupIds.get(targetUserId,
UserInfo.NO_PROFILE_GROUP_ID);
return callingProfile != UserInfo.NO_PROFILE_GROUP_ID
&& callingProfile == targetProfile;
}
}
boolean isUserOrItsParentRunning(@UserIdInt int userId) {
synchronized (mLock) {
if (isUserRunning(userId, 0)) {
return true;
}
final int parentUserId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID);
if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
return false;
}
return isUserRunning(parentUserId, 0);
}
}
boolean isCurrentProfile(@UserIdInt int userId) {
synchronized (mLock) {
return ArrayUtils.contains(mCurrentProfileIds, userId);
}
}
int[] getCurrentProfileIds() {
synchronized (mLock) {
return mCurrentProfileIds;
}
}
private void onUserAdded(UserInfo user) {
if (!user.isProfile()) {
return;
}
synchronized (mLock) {
if (user.profileGroupId == mCurrentUserId) {
mCurrentProfileIds = ArrayUtils.appendInt(mCurrentProfileIds, user.id);
}
if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
mUserProfileGroupIds.put(user.id, user.profileGroupId);
}
}
}
void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
int size = mUserProfileGroupIds.size();
for (int i = size - 1; i >= 0; i--) {
if (mUserProfileGroupIds.keyAt(i) == userId
|| mUserProfileGroupIds.valueAt(i) == userId) {
mUserProfileGroupIds.removeAt(i);
}
}
mCurrentProfileIds = ArrayUtils.removeInt(mCurrentProfileIds, userId);
}
}
/**
* Returns whether the given user requires credential entry at this time. This is used to
* intercept activity launches for locked work apps due to work challenge being triggered
* or when the profile user is yet to be unlocked.
*/
protected boolean shouldConfirmCredentials(@UserIdInt int userId) {
if (getStartedUserState(userId) == null) {
return false;
}
final UserProperties properties = getUserProperties(userId);
if (properties == null || !properties.isCredentialShareableWithParent()) {
return false;
}
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
final KeyguardManager km = mInjector.getKeyguardManager();
return km.isDeviceLocked(userId) && km.isDeviceSecure(userId);
} else {
// For unified challenge, need to confirm credential if user is RUNNING_LOCKED.
return isUserRunning(userId, ActivityManager.FLAG_AND_LOCKED);
}
}
boolean isLockScreenDisabled(@UserIdInt int userId) {
return mLockPatternUtils.isLockScreenDisabled(userId);
}
void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
synchronized (mLock) {
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
}
}
void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
synchronized (mLock) {
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
}
}
// Called by AMS, must check permission
String getSwitchingFromSystemUserMessage() {
checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
return getSwitchingFromSystemUserMessageUnchecked();
}
// Called by AMS, must check permission
String getSwitchingToSystemUserMessage() {
checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
return getSwitchingToSystemUserMessageUnchecked();
}
private String getSwitchingFromSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingFromSystemUserMessage;
}
}
private String getSwitchingToSystemUserMessageUnchecked() {
synchronized (mLock) {
return mSwitchingToSystemUserMessage;
}
}
private void checkHasManageUsersPermission(String operation) {
if (mInjector.checkCallingPermission(
android.Manifest.permission.MANAGE_USERS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"You need MANAGE_USERS permission to call " + operation);
}
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
long token = proto.start(fieldId);
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
final long uToken = proto.start(UserControllerProto.STARTED_USERS);
proto.write(UserControllerProto.User.ID, uss.mHandle.getIdentifier());
uss.dumpDebug(proto, UserControllerProto.User.STATE);
proto.end(uToken);
}
for (int i = 0; i < mStartedUserArray.length; i++) {
proto.write(UserControllerProto.STARTED_USER_ARRAY, mStartedUserArray[i]);
}
for (int i = 0; i < mUserLru.size(); i++) {
proto.write(UserControllerProto.USER_LRU, mUserLru.get(i));
}
if (mUserProfileGroupIds.size() > 0) {
for (int i = 0; i < mUserProfileGroupIds.size(); i++) {
final long uToken = proto.start(UserControllerProto.USER_PROFILE_GROUP_IDS);
proto.write(UserControllerProto.UserProfile.USER,
mUserProfileGroupIds.keyAt(i));
proto.write(UserControllerProto.UserProfile.PROFILE,
mUserProfileGroupIds.valueAt(i));
proto.end(uToken);
}
}
proto.write(UserControllerProto.CURRENT_USER, mCurrentUserId);
for (int i = 0; i < mCurrentProfileIds.length; i++) {
proto.write(UserControllerProto.CURRENT_PROFILES, mCurrentProfileIds[i]);
}
proto.end(token);
}
}
void dump(PrintWriter pw) {
synchronized (mLock) {
pw.println(" mStartedUsers:");
for (int i = 0; i < mStartedUsers.size(); i++) {
UserState uss = mStartedUsers.valueAt(i);
pw.print(" User #");
pw.print(uss.mHandle.getIdentifier());
pw.print(": ");
uss.dump("", pw);
}
pw.print(" mStartedUserArray: [");
for (int i = 0; i < mStartedUserArray.length; i++) {
if (i > 0)
pw.print(", ");
pw.print(mStartedUserArray[i]);
}
pw.println("]");
pw.print(" mUserLru: [");
for (int i = 0; i < mUserLru.size(); i++) {
if (i > 0)
pw.print(", ");
pw.print(mUserLru.get(i));
}
pw.println("]");
if (mUserProfileGroupIds.size() > 0) {
pw.println(" mUserProfileGroupIds:");
for (int i=0; i< mUserProfileGroupIds.size(); i++) {
pw.print(" User #");
pw.print(mUserProfileGroupIds.keyAt(i));
pw.print(" -> profile #");
pw.println(mUserProfileGroupIds.valueAt(i));
}
}
pw.println(" mCurrentProfileIds:" + Arrays.toString(mCurrentProfileIds));
pw.println(" mCurrentUserId:" + mCurrentUserId);
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking);
pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
pw.println(" mIsBroadcastSentForSystemUserStarted:"
+ mIsBroadcastSentForSystemUserStarted);
pw.println(" mIsBroadcastSentForSystemUserStarting:"
+ mIsBroadcastSentForSystemUserStarting);
if (mSwitchingFromSystemUserMessage != null) {
pw.println(" mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
}
if (mSwitchingToSystemUserMessage != null) {
pw.println(" mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
}
pw.println(" mLastUserUnlockingUptime: " + mLastUserUnlockingUptime);
}
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case START_USER_SWITCH_FG_MSG:
logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_SWITCH_FG);
startUserInForeground(msg.arg1);
break;
case REPORT_USER_SWITCH_MSG:
dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
case CONTINUE_USER_SWITCH_MSG:
continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
case USER_SWITCH_TIMEOUT_MSG:
timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2);
break;
case USER_SWITCH_CALLBACKS_TIMEOUT_MSG:
timeoutUserSwitchCallbacks(msg.arg1, msg.arg2);
break;
case START_PROFILES_MSG:
startProfiles();
break;
case USER_START_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
logUserJourneyBegin(msg.arg1, USER_JOURNEY_USER_START);
mInjector.onUserStarting(/* userId= */ msg.arg1);
scheduleOnUserCompletedEvent(msg.arg1,
UserCompletedEventType.EVENT_TYPE_USER_STARTING,
USER_COMPLETED_EVENT_DELAY_MS);
mInjector.getUserJourneyLogger().logUserJourneyFinish(-1 , getUserInfo(msg.arg1),
USER_JOURNEY_USER_START);
break;
case USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().onUserUnlocking(userId);
// Loads recents on a worker thread that allows disk I/O
FgThread.getHandler().post(() -> {
mInjector.loadUserRecents(userId);
});
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
USER_LIFECYCLE_EVENT_UNLOCKING_USER, EVENT_STATE_FINISH);
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_BEGIN);
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("finishUserUnlocked-" + userId);
finishUserUnlocked((UserState) msg.obj);
t.traceEnd();
break;
case USER_UNLOCKED_MSG:
mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
scheduleOnUserCompletedEvent(msg.arg1,
UserCompletedEventType.EVENT_TYPE_USER_UNLOCKED,
// If it's the foreground user, we wait longer to let it fully load.
// Else, there's nothing specific to wait for, so we basically just proceed.
// (No need to acquire lock to read mCurrentUserId since it is volatile.)
// TODO: Find something to wait for in the case of a profile.
mCurrentUserId == msg.arg1 ? USER_COMPLETED_EVENT_DELAY_MS : 1000);
mInjector.getUserJourneyLogger().logUserLifecycleEvent(msg.arg1,
USER_LIFECYCLE_EVENT_UNLOCKED_USER, EVENT_STATE_FINISH);
// Unlocking user is not a journey no need to clear sessionId
break;
case USER_CURRENT_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
Integer.toString(msg.arg2), msg.arg2);
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
Integer.toString(msg.arg1), msg.arg1);
mInjector.getSystemServiceManager().onUserSwitching(msg.arg2, msg.arg1);
scheduleOnUserCompletedEvent(msg.arg1,
UserCompletedEventType.EVENT_TYPE_USER_SWITCHING,
USER_COMPLETED_EVENT_DELAY_MS);
break;
case USER_COMPLETED_EVENT_MSG:
reportOnUserCompletedEvent((Integer) msg.obj);
break;
case FOREGROUND_PROFILE_CHANGED_MSG:
dispatchForegroundProfileChanged(msg.arg1);
break;
case REPORT_USER_SWITCH_COMPLETE_MSG:
dispatchUserSwitchComplete(msg.arg1, msg.arg2);
UserJourneySession session = mInjector.getUserJourneyLogger()
.logUserSwitchJourneyFinish(msg.arg1, getUserInfo(msg.arg2));
if (session != null) {
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, session);
}
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
dispatchLockedBootComplete(msg.arg1);
break;
case START_USER_SWITCH_UI_MSG:
final Pair<UserInfo, UserInfo> fromToUserPair = (Pair<UserInfo, UserInfo>) msg.obj;
logUserJourneyBegin(fromToUserPair.second.id, USER_JOURNEY_USER_SWITCH_UI);
showUserSwitchDialog(fromToUserPair);
break;
case CLEAR_USER_JOURNEY_SESSION_MSG:
mInjector.getUserJourneyLogger()
.finishAndClearIncompleteUserJourney(msg.arg1, msg.arg2);
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, msg.obj);
break;
case COMPLETE_USER_SWITCH_MSG:
completeUserSwitch(msg.arg1, msg.arg2);
break;
}
return false;
}
/**
* Schedules {@link SystemServiceManager#onUserCompletedEvent()} with the given
* {@link UserCompletedEventType} event, which will be combined with any other events for that
* user already scheduled.
* If it isn't rescheduled first, it will fire after a delayMs delay.
*
* @param eventType event type flags from {@link UserCompletedEventType} to append to the
* schedule. Use 0 to schedule the ssm call without modifying the event types.
*/
// TODO(b/197344658): Also call scheduleOnUserCompletedEvent(userId, 0, 0) after switch UX done.
@VisibleForTesting
void scheduleOnUserCompletedEvent(
int userId, @UserCompletedEventType.EventTypesFlag int eventType, int delayMs) {
if (eventType != 0) {
synchronized (mCompletedEventTypes) {
mCompletedEventTypes.put(userId, mCompletedEventTypes.get(userId, 0) | eventType);
}
}
final Object msgObj = userId;
mHandler.removeEqualMessages(USER_COMPLETED_EVENT_MSG, msgObj);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(USER_COMPLETED_EVENT_MSG, msgObj),
delayMs);
}
/**
* Calls {@link SystemServiceManager#onUserCompletedEvent()} for the given user, sending all the
* {@link UserCompletedEventType} events that have been scheduled for it if they are still
* applicable.
*
* Called on the mHandler thread.
*/
@VisibleForTesting
void reportOnUserCompletedEvent(Integer userId) {
mHandler.removeEqualMessages(USER_COMPLETED_EVENT_MSG, userId);
int eventTypes;
synchronized (mCompletedEventTypes) {
eventTypes = mCompletedEventTypes.get(userId, 0);
mCompletedEventTypes.delete(userId);
}
// Now, remove any eventTypes that are no longer true.
int eligibleEventTypes = 0;
synchronized (mLock) {
final UserState uss = mStartedUsers.get(userId);
if (uss != null && uss.state != UserState.STATE_SHUTDOWN) {
eligibleEventTypes |= UserCompletedEventType.EVENT_TYPE_USER_STARTING;
}
if (uss != null && uss.state == STATE_RUNNING_UNLOCKED) {
eligibleEventTypes |= UserCompletedEventType.EVENT_TYPE_USER_UNLOCKED;
}
if (userId == mCurrentUserId) {
eligibleEventTypes |= UserCompletedEventType.EVENT_TYPE_USER_SWITCHING;
}
}
Slogf.i(TAG, "reportOnUserCompletedEvent(%d): stored=%s, eligible=%s", userId,
Integer.toBinaryString(eventTypes), Integer.toBinaryString(eligibleEventTypes));
eventTypes &= eligibleEventTypes;
mInjector.systemServiceManagerOnUserCompletedEvent(userId, eventTypes);
}
/**
* statsd helper method for logging the start of a user journey via a UserLifecycleEventOccurred
* atom given the originating and targeting users for the journey.
*/
private void logUserJourneyBegin(int targetId,
@UserJourneyLogger.UserJourney int journey) {
UserJourneySession oldSession = mInjector.getUserJourneyLogger()
.finishAndClearIncompleteUserJourney(targetId, journey);
if (oldSession != null) {
if (DEBUG_MU) {
Slogf.d(TAG,
"Starting a new journey: " + journey + " with session id: "
+ oldSession);
}
/*
* User lifecycle journey would be complete when {@code #clearSessionId} is called
* after the last expected lifecycle event for the journey. It may be possible that
* the last event is not called, e.g., user not unlocked after user switching. In such
* cases user journey is cleared after {@link USER_JOURNEY_TIMEOUT}.
*/
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG, oldSession);
}
UserJourneySession newSession = mInjector.getUserJourneyLogger()
.logUserJourneyBegin(targetId, journey);
mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG,
targetId, /* arg2= */ journey, newSession), USER_JOURNEY_TIMEOUT_MS);
}
BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@PowerWhitelistManager.ReasonCode int reasonCode) {
long duration = 10_000;
final ActivityManagerInternal amInternal =
LocalServices.getService(ActivityManagerInternal.class);
if (amInternal != null) {
duration = amInternal.getBootTimeTempAllowListDuration();
}
final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
bOptions.setTemporaryAppAllowlist(duration,
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, reasonCode, "");
return bOptions;
}
private static int getUserSwitchTimeoutMs() {
final String userSwitchTimeoutMs = SystemProperties.get(
"debug.usercontroller.user_switch_timeout_ms");
if (!TextUtils.isEmpty(userSwitchTimeoutMs)) {
try {
return Integer.parseInt(userSwitchTimeoutMs);
} catch (NumberFormatException ignored) {
// Ignored.
}
}
return DEFAULT_USER_SWITCH_TIMEOUT_MS;
}
private static void asyncTraceBegin(String msg, int cookie) {
Slogf.d(TAG, "%s - asyncTraceBegin(%d)", msg, cookie);
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
}
private static void asyncTraceEnd(String msg, int cookie) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
Slogf.d(TAG, "%s - asyncTraceEnd(%d)", msg, cookie);
}
/**
* Uptime when any user was being unlocked most recently. 0 if no users have been unlocked
* yet. To avoid lock contention (since it's used by OomAdjuster), it's volatile internally.
*/
public long getLastUserUnlockingUptime() {
return mLastUserUnlockingUptime;
}
private static class UserProgressListener extends IProgressListener.Stub {
private volatile long mUnlockStarted;
@Override
public void onStarted(int id, Bundle extras) throws RemoteException {
Slogf.d(TAG, "Started unlocking user " + id);
mUnlockStarted = SystemClock.uptimeMillis();
}
@Override
public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
Slogf.d(TAG, "Unlocking user " + id + " progress " + progress);
}
@Override
public void onFinished(int id, Bundle extras) throws RemoteException {
long unlockTime = SystemClock.uptimeMillis() - mUnlockStarted;
// Report system user unlock time to perf dashboard
if (id == UserHandle.USER_SYSTEM) {
new TimingsTraceAndSlog().logDuration("SystemUserUnlock", unlockTime);
} else {
new TimingsTraceAndSlog().logDuration("User" + id + "Unlock", unlockTime);
}
}
}
/**
* Helper class for keeping track of user starts which are paused while user's
* shutdown is taking place.
*/
private static class PendingUserStart {
public final @UserIdInt int userId;
public final @UserStartMode int userStartMode;
public final IProgressListener unlockListener;
PendingUserStart(int userId, @UserStartMode int userStartMode,
IProgressListener unlockListener) {
this.userId = userId;
this.userStartMode = userStartMode;
this.unlockListener = unlockListener;
}
@Override
public String toString() {
return "PendingUserStart{"
+ "userId=" + userId
+ ", userStartMode=" + userStartModeToString(userStartMode)
+ ", unlockListener=" + unlockListener
+ '}';
}
}
@VisibleForTesting
static class Injector {
private final ActivityManagerService mService;
private UserManagerService mUserManager;
private UserManagerInternal mUserManagerInternal;
private Handler mHandler;
private final Object mUserSwitchingDialogLock = new Object();
@GuardedBy("mUserSwitchingDialogLock")
private UserSwitchingDialog mUserSwitchingDialog;
Injector(ActivityManagerService service) {
mService = service;
}
protected Handler getHandler(Handler.Callback callback) {
return mHandler = new Handler(mService.mHandlerThread.getLooper(), callback);
}
protected Handler getUiHandler(Handler.Callback callback) {
return new Handler(mService.mUiHandler.getLooper(), callback);
}
protected UserJourneyLogger getUserJourneyLogger() {
return getUserManager().getUserJourneyLogger();
}
protected Context getContext() {
return mService.mContext;
}
protected LockPatternUtils getLockPatternUtils() {
return new LockPatternUtils(getContext());
}
protected int broadcastIntent(Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, @UserIdInt int userId) {
int logUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (logUserId == UserHandle.USER_NULL) {
logUserId = userId;
}
EventLog.writeEvent(EventLogTags.UC_SEND_USER_BROADCAST, logUserId, intent.getAction());
// When the modern broadcast stack is enabled, deliver all our
// broadcasts as unordered, since the modern stack has better
// support for sequencing cold-starts, and it supports delivering
// resultTo for non-ordered broadcasts
if (mService.mEnableModernQueue) {
ordered = false;
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
// TODO b/64165549 Verify that mLock is not held before calling AMS methods
synchronized (mService) {
t.traceBegin("broadcastIntent-" + userId + "-" + intent.getAction());
final int result = mService.broadcastIntentLocked(null, null, null, intent,
resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, null, null, appOp, bOptions, ordered, sticky,
callingPid, callingUid, realCallingUid, realCallingPid, userId);
t.traceEnd();
return result;
}
}
int checkCallingPermission(String permission) {
return mService.checkCallingPermission(permission);
}
WindowManagerService getWindowManager() {
return mService.mWindowManager;
}
ActivityTaskManagerInternal getActivityTaskManagerInternal() {
return mService.mAtmInternal;
}
void activityManagerOnUserStopped(@UserIdInt int userId) {
LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
}
void systemServiceManagerOnUserStopped(@UserIdInt int userId) {
getSystemServiceManager().onUserStopped(userId);
}
void systemServiceManagerOnUserCompletedEvent(@UserIdInt int userId, int eventTypes) {
getSystemServiceManager().onUserCompletedEvent(userId, eventTypes);
}
protected UserManagerService getUserManager() {
if (mUserManager == null) {
IBinder b = ServiceManager.getService(Context.USER_SERVICE);
mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
}
return mUserManager;
}
UserManagerInternal getUserManagerInternal() {
if (mUserManagerInternal == null) {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
return mUserManagerInternal;
}
KeyguardManager getKeyguardManager() {
return mService.mContext.getSystemService(KeyguardManager.class);
}
void batteryStatsServiceNoteEvent(int code, String name, int uid) {
mService.mBatteryStatsService.noteEvent(code, name, uid);
}
boolean isRuntimeRestarted() {
return getSystemServiceManager().isRuntimeRestarted();
}
SystemServiceManager getSystemServiceManager() {
return mService.mSystemServiceManager;
}
boolean isFirstBootOrUpgrade() {
IPackageManager pm = AppGlobals.getPackageManager();
try {
return pm.isFirstBoot() || pm.isDeviceUpgrading();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
void sendPreBootBroadcast(@UserIdInt int userId, boolean quiet, final Runnable onFinish) {
EventLog.writeEvent(EventLogTags.UC_SEND_USER_BROADCAST,
userId, Intent.ACTION_PRE_BOOT_COMPLETED);
new PreBootBroadcaster(mService, userId, null, quiet) {
@Override
public void onFinished() {
onFinish.run();
}
}.sendNext();
}
void activityManagerForceStopPackage(@UserIdInt int userId, String reason) {
synchronized (mService) {
mService.forceStopPackageLocked(null, -1, false, false, true, false, false, false,
userId, reason);
}
};
int checkComponentPermission(String permission, int pid, int uid, int owningUid,
boolean exported) {
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
return PermissionChecker.PERMISSION_GRANTED
== PermissionChecker.checkPermissionForPreflight(
getContext(), permission, pid, uid, pkg);
}
protected void startHomeActivity(@UserIdInt int userId, String reason) {
mService.mAtmInternal.startHomeActivity(userId, reason);
}
void startUserWidgets(@UserIdInt int userId) {
AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class);
if (awm != null) {
// Out of band, because this is called during a sequence with
// sensitive cross-service lock management
FgThread.getHandler().post(() -> {
awm.unlockUser(userId);
});
}
}
void updateUserConfiguration() {
mService.mAtmInternal.updateUserConfiguration();
}
void clearBroadcastQueueForUser(@UserIdInt int userId) {
synchronized (mService) {
mService.clearBroadcastQueueForUserLocked(userId);
}
}
void loadUserRecents(@UserIdInt int userId) {
mService.mAtmInternal.loadRecentTasksForUser(userId);
}
void startPersistentApps(int matchFlags) {
mService.startPersistentApps(matchFlags);
}
void installEncryptionUnawareProviders(@UserIdInt int userId) {
mService.mCpHelper.installEncryptionUnawareProviders(userId);
}
void dismissUserSwitchingDialog(@Nullable Runnable onDismissed) {
synchronized (mUserSwitchingDialogLock) {
if (mUserSwitchingDialog != null) {
mUserSwitchingDialog.dismiss(onDismissed);
mUserSwitchingDialog = null;
} else if (onDismissed != null) {
onDismissed.run();
}
}
}
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
@NonNull Runnable onShown) {
if (mService.mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
// config_customUserSwitchUi is set to true on Automotive as CarSystemUI is
// responsible to show the UI; OEMs should not change that, but if they do, we
// should at least warn the user...
Slogf.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
+ "condition if it's shown by CarSystemUI as well");
}
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
switchingFromSystemUserMessage, switchingToSystemUserMessage,
getWindowManager());
mUserSwitchingDialog.show(onShown);
}
}
void reportGlobalUsageEvent(int event) {
mService.reportGlobalUsageEvent(event);
}
void reportCurWakefulnessUsageEvent() {
mService.reportCurWakefulnessUsageEvent();
}
void taskSupervisorRemoveUser(@UserIdInt int userId) {
mService.mAtmInternal.removeUser(userId);
}
protected boolean taskSupervisorSwitchUser(@UserIdInt int userId, UserState uss) {
return mService.mAtmInternal.switchUser(userId, uss);
}
protected void taskSupervisorResumeFocusedStackTopActivity() {
mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
}
protected void clearAllLockedTasks(String reason) {
mService.mAtmInternal.clearLockedTasks(reason);
}
boolean isCallerRecents(int callingUid) {
return mService.mAtmInternal.isCallerRecents(callingUid);
}
protected IStorageManager getStorageManager() {
return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
}
protected void dismissKeyguard(Runnable runnable) {
final AtomicBoolean isFirst = new AtomicBoolean(true);
final Runnable runOnce = () -> {
if (isFirst.getAndSet(false)) {
runnable.run();
}
};
mHandler.postDelayed(runOnce, DISMISS_KEYGUARD_TIMEOUT_MS);
getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() {
@Override
public void onDismissError() throws RemoteException {
mHandler.post(runOnce);
}
@Override
public void onDismissSucceeded() throws RemoteException {
mHandler.post(runOnce);
}
@Override
public void onDismissCancelled() throws RemoteException {
mHandler.post(runOnce);
}
}, /* message= */ null);
}
boolean isHeadlessSystemUserMode() {
return UserManager.isHeadlessSystemUserMode();
}
boolean isUsersOnSecondaryDisplaysEnabled() {
return UserManager.isVisibleBackgroundUsersEnabled();
}
void onUserStarting(@UserIdInt int userId) {
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
void setHasTopUi(boolean hasTopUi) {
try {
Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi);
mService.setHasTopUi(hasTopUi);
} catch (RemoteException e) {
Slogf.e(TAG, "Failed to allow using all CPU cores", e);
}
}
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
void lockDeviceNowAndWaitForKeyguardShown() {
if (getWindowManager().isKeyguardLocked()) {
return;
}
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("lockDeviceNowAndWaitForKeyguardShown");
final CountDownLatch latch = new CountDownLatch(1);
ActivityTaskManagerInternal.ScreenObserver screenObserver =
new ActivityTaskManagerInternal.ScreenObserver() {
@Override
public void onAwakeStateChanged(boolean isAwake) {
}
@Override
public void onKeyguardStateChanged(boolean isShowing) {
if (isShowing) {
latch.countDown();
}
}
};
getActivityTaskManagerInternal().registerScreenObserver(screenObserver);
getWindowManager().lockDeviceNow();
try {
if (!latch.await(20, TimeUnit.SECONDS)) {
throw new RuntimeException("Keyguard is not shown in 20 seconds");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver);
t.traceEnd();
}
}
}
}