blob: fecb93456605aece4e0c1a59f380b719629c5b8c [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_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
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.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
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.ALLOW_FULL_ONLY;
import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
import android.app.usage.UsageEvents;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
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.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimingsTraceLog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 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 unfreezing the screen.
static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000;
// 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 SYSTEM_USER_START_MSG = 50;
static final int SYSTEM_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 SYSTEM_USER_UNLOCK_MSG = 100;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
static final int START_USER_SWITCH_FG_MSG = 120;
// 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
// USER_SWITCH_TIMEOUT_MS, 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;
/**
* 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
*/
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;
/**
* 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 };
// If there are multiple profiles for the current user, their ids are here
// Currently only the primary user can have managed profiles
@GuardedBy("mLock")
private int[] mCurrentProfileIds = new int[] {};
/**
* Mapping from each known user ID to the profile group ID it is associated with.
*/
@GuardedBy("mLock")
private final SparseIntArray mUserProfileGroupIds = new SparseIntArray();
/**
* Registered observers of the user switching mechanics.
*/
private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
= new RemoteCallbackList<>();
boolean mUserSwitchUiEnabled = true;
/**
* Currently active user switch callbacks.
*/
@GuardedBy("mLock")
private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
/**
* Messages for for switching from {@link android.os.UserHandle#SYSTEM}.
*/
@GuardedBy("mLock")
private String mSwitchingFromSystemUserMessage;
/**
* Messages for for switching to {@link android.os.UserHandle#SYSTEM}.
*/
@GuardedBy("mLock")
private String mSwitchingToSystemUserMessage;
/**
* Callbacks that are still active after {@link #USER_SWITCH_TIMEOUT_MS}
*/
@GuardedBy("mLock")
private ArraySet<String> mTimeoutUserSwitchCallbacks;
private final LockPatternUtils mLockPatternUtils;
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@VisibleForTesting
UserController(Injector injector) {
mInjector = 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 finishUserSwitch(UserState uss) {
finishUserBoot(uss);
startProfiles();
synchronized (mLock) {
stopRunningUsersLU(mMaxRunningUsers);
}
}
List<Integer> getRunningUsersLU() {
ArrayList<Integer> runningUsers = new ArrayList<>();
for (Integer userId : mUserLru) {
UserState uss = mStartedUsers.get(userId);
if (uss == null) {
// Shouldn't happen, but be sane if it does.
continue;
}
if (uss.state == UserState.STATE_STOPPING
|| uss.state == UserState.STATE_SHUTDOWN) {
// This user is already stopping, doesn't count.
continue;
}
if (userId == UserHandle.USER_SYSTEM) {
// We only count system user as running when it is not a pure system user.
if (UserInfo.isSystemOnly(userId)) {
continue;
}
}
runningUsers.add(userId);
}
return runningUsers;
}
void stopRunningUsersLU(int maxRunningUsers) {
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) {
// Owner/System user and current user can't be stopped
continue;
}
if (stopUsersLU(userId, false, 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();
Slog.d(TAG, "Finishing 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.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()) {
int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
MetricsLogger.histogram(mInjector.getContext(),
"framework_locked_boot_completed", uptimeSeconds);
final int MAX_UPTIME_SECONDS = 120;
if (uptimeSeconds > MAX_UPTIME_SECONDS) {
Slog.wtf("SystemServerTiming",
"finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
}
}
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
userId, 0));
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_INCLUDE_BACKGROUND);
mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
// We need to delay unlocking managed profiles until the parent user
// is also unlocked.
if (mInjector.getUserManager().isManagedProfile(userId)) {
final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null
&& isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
Slog.d(TAG, "User " + userId + " (parent " + parent.id
+ "): attempting unlock because parent is unlocked");
maybeUnlockUser(userId);
} else {
String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
Slog.d(TAG, "User " + userId + " (parent " + parentId
+ "): delaying unlock because parent is locked");
}
} else {
maybeUnlockUser(userId);
}
}
/**
* Step from {@link UserState#STATE_RUNNING_LOCKED} to
* {@link UserState#STATE_RUNNING_UNLOCKING}.
*/
private void finishUserUnlocking(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
// Only keep marching forward if user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
// Do not proceed if unexpected state or a stale user
if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
return;
}
}
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(() -> {
mInjector.getUserManager().onBeforeUnlockUser(userId);
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);
// Dispatch unlocked to system services; when fully dispatched,
// that calls through to the next "unlocked" phase
mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
.sendToTarget();
});
}
/**
* Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
* {@link UserState#STATE_RUNNING_UNLOCKED}.
*/
void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
// Only keep marching forward if user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(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);
// 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,
userId);
if (getUserInfo(userId).isManagedProfile()) {
UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
if (parent != null) {
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,
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, Build.FINGERPRINT)) {
// Suppress double notifications for managed profiles that
// were unlocked automatically as part of their parent user
// being unlocked.
final boolean quiet;
if (info.isManagedProfile()) {
quiet = !uss.tokenProvided
|| !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
} else {
quiet = false;
}
mInjector.sendPreBootBroadcast(userId, quiet,
() -> finishUserUnlockedCompleted(uss));
} else {
finishUserUnlockedCompleted(uss);
}
}
private void finishUserUnlockedCompleted(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
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 user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(userId)) return;
// Remember that we logged in
mInjector.getUserManager().onUserLoggedIn(userId);
if (!userInfo.isInitialized()) {
if (userId != UserHandle.USER_SYSTEM) {
Slog.d(TAG, "Initializing user #" + userId);
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
mInjector.getUserManager().makeInitialized(userInfo.id);
}
}, 0, null, null, null, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, userId);
}
}
Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
uptimeSeconds);
}
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);
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 {
Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
}
}, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
int restartUser(final int userId, final boolean foreground) {
return stopUser(userId, /* force */ true, new IStopUserCallback.Stub() {
@Override
public void userStopped(final int userId) {
// Post to the same handler that this callback is called from to ensure the user
// cleanup is complete before restarting.
mHandler.post(() -> startUser(userId, foreground));
}
@Override
public void userStopAborted(final int userId) {}
});
}
int stopUser(final int userId, final boolean force, final IStopUserCallback callback) {
if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: switchUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
throw new IllegalArgumentException("Can't stop system user " + userId);
}
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
return stopUsersLU(userId, force, callback);
}
}
/**
* Stops the user along with its related users. The method calls
* {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
*/
private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) {
if (userId == UserHandle.USER_SYSTEM) {
return USER_OP_ERROR_IS_SYSTEM;
}
if (isCurrentUserLU(userId)) {
return USER_OP_IS_CURRENT;
}
int[] usersToStop = getUsersToStopLU(userId);
// If one of related users is system or current, no related users should be stopped
for (int i = 0; i < usersToStop.length; i++) {
int relatedUserId = usersToStop[i];
if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
+ relatedUserId);
// We still need to stop the requested user if it's a force stop.
if (force) {
Slog.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
stopSingleUserLU(userId, callback);
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
}
}
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
stopSingleUserLU(userIdToStop, userIdToStop == userId ? callback : null);
}
return USER_OP_SUCCESS;
}
private void stopSingleUserLU(final int userId, final IStopUserCallback callback) {
if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) {
// User is not started, nothing to do... but we do need to
// callback if requested.
if (callback != null) {
mHandler.post(() -> {
try {
callback.userStopped(userId);
} catch (RemoteException e) {
}
});
}
return;
}
if (callback != null) {
uss.mStopCallbacks.add(callback);
}
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
updateStartedUserArrayLU();
// 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) {
mHandler.post(() -> finishUserStopping(userId, uss));
}
};
// Clear broadcast queue for the user to avoid delivering stale broadcasts
mInjector.clearBroadcastQueueForUser(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, UserHandle.USER_ALL);
});
}
}
void finishUserStopping(final int userId, final UserState uss) {
// On to the next.
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) {
mHandler.post(new Runnable() {
@Override
public void run() {
finishUserStopped(uss);
}
});
}
};
synchronized (mLock) {
if (uss.state != UserState.STATE_STOPPING) {
// Whoops, we are being started back up. Abort, abort!
return;
}
uss.setState(UserState.STATE_SHUTDOWN);
}
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
Integer.toString(userId), userId);
mInjector.getSystemServiceManager().stopUser(userId);
mInjector.broadcastIntent(shutdownIntent,
null, shutdownReceiver, 0, null, null, null,
AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, userId);
}
void finishUserStopped(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
boolean stopped;
ArrayList<IStopUserCallback> callbacks;
boolean forceStopUser = false;
synchronized (mLock) {
callbacks = new ArrayList<>(uss.mStopCallbacks);
if (mStartedUsers.get(userId) != uss) {
stopped = false;
} else if (uss.state != UserState.STATE_SHUTDOWN) {
stopped = false;
} else {
stopped = true;
// User can no longer run.
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLU();
forceStopUser = true;
}
}
if (forceStopUser) {
mInjector.getUserManagerInternal().removeUserState(userId);
mInjector.activityManagerOnUserStopped(userId);
// Clean up all state and processes associated with the user.
// Kill all the processes for the user.
forceStopUser(userId, "finish user");
}
for (int i = 0; i < callbacks.size(); i++) {
try {
if (stopped) callbacks.get(i).userStopped(userId);
else callbacks.get(i).userStopAborted(userId);
} catch (RemoteException e) {
}
}
if (stopped) {
mInjector.systemServiceManagerCleanupUser(userId);
mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
if (getUserInfo(userId).isEphemeral()) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
// Evict the user's credential encryption key.
try {
getStorageManager().lockUserKey(userId);
} catch (RemoteException re) {
throw re.rethrowAsRuntimeException();
}
}
}
/**
* Determines the list of users that should be stopped together with the specified
* {@code userId}. The returned list includes {@code userId}.
*/
private @NonNull int[] getUsersToStopLU(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(int userId, String reason) {
mInjector.activityManagerForceStopPackage(userId, reason);
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, UserHandle.USER_ALL);
}
/**
* Stops the guest or ephemeral user if it has gone to the background.
*/
private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
if (DEBUG_MU) Slog.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, true, null);
}
}
}
void scheduleStartProfiles() {
if (!mHandler.hasMessages(START_PROFILES_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG),
DateUtils.SECOND_IN_MILLIS);
}
}
void startProfiles() {
int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slog.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 && !user.isQuietModeEnabled()) {
profilesToStart.add(user);
}
}
final int profilesToStartSize = profilesToStart.size();
int i = 0;
for (; i < profilesToStartSize && i < (mMaxRunningUsers - 1); ++i) {
startUser(profilesToStart.get(i).id, /* foreground= */ false);
}
if (i < profilesToStartSize) {
Slog.w(TAG, "More profiles than MAX_RUNNING_USERS");
}
}
private IStorageManager getStorageManager() {
return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
}
boolean startUser(final int userId, final boolean foreground) {
return startUser(userId, foreground, null);
}
/**
* Start user, if its not already running.
* <p>The user will be brought to the foreground, if {@code foreground} parameter is set.
* 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 true
* <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 true
* <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user.
* Sent only if {@code foreground} parameter is 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 foreground true if user should be brought to the foreground
* @return true if the user has been successfully started
*/
boolean startUser(
final int userId,
final boolean foreground,
@Nullable IProgressListener unlockListener) {
if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: switchUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground);
final long ident = Binder.clearCallingIdentity();
try {
final int oldUserId = getCurrentUserId();
if (oldUserId == userId) {
return true;
}
if (foreground) {
mInjector.clearAllLockedTasks("startUser");
}
final UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
Slog.w(TAG, "No user info for user #" + userId);
return false;
}
if (foreground && userInfo.isManagedProfile()) {
Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
return false;
}
if (foreground && mUserSwitchUiEnabled) {
mInjector.getWindowManager().startFreezingScreen(
R.anim.screen_user_exit, R.anim.screen_user_enter);
}
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.
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;
}
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
}
if (unlockListener != null) {
uss.mUnlockProgress.addListener(unlockListener);
}
if (updateUmState) {
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
}
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
synchronized (mLock) {
mCurrentUserId = userId;
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
}
mInjector.updateUserConfiguration();
updateCurrentProfileIds();
mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
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 (mUserSwitchUiEnabled) {
mInjector.getWindowManager().setSwitchingUser(true);
mInjector.getWindowManager().lockNow(null);
}
} else {
final Integer currentUserIdInt = mCurrentUserId;
updateCurrentProfileIds();
mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds());
synchronized (mLock) {
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
}
}
// 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) {
// 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;
} else if (uss.state == UserState.STATE_SHUTDOWN) {
// 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;
}
if (uss.state == UserState.STATE_BOOTING) {
// 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(SYSTEM_USER_START_MSG, userId, 0));
}
if (foreground) {
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_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), USER_SWITCH_TIMEOUT_MS);
}
if (needStart) {
// Send USER_STARTED broadcast
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,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, userId);
}
if (foreground) {
moveUserToForeground(uss, oldUserId, userId);
} else {
finishUserBoot(uss);
}
if (needStart) {
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,
null, new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky,
int sendingUser) throws RemoteException {
}
}, 0, null, null,
new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return true;
}
/**
* Start user, if its not already running, and bring it to foreground.
*/
void startUserInForeground(final int targetUserId) {
boolean success = startUser(targetUserId, /* foreground */ true);
if (!success) {
mInjector.getWindowManager().setSwitchingUser(false);
}
}
boolean unlockUser(final int userId, byte[] token, byte[] secret, IProgressListener listener) {
if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: unlockUser() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final long binderToken = Binder.clearCallingIdentity();
try {
return unlockUserCleared(userId, token, secret, listener);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
/**
* Attempt to unlock user without a credential token. This typically
* succeeds when the device doesn't have credential-encrypted storage, or
* when the the credential-encrypted storage isn't tied to a user-provided
* PIN or pattern.
*/
private boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
return unlockUserCleared(userId, null, null, null);
}
private static void notifyFinished(int userId, IProgressListener listener) {
if (listener == null) return;
try {
listener.onFinished(userId, null);
} catch (RemoteException ignored) {
}
}
private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
IProgressListener listener) {
UserState uss;
if (!StorageManager.isUserKeyUnlocked(userId)) {
final UserInfo userInfo = getUserInfo(userId);
final IStorageManager storageManager = getStorageManager();
try {
// We always want to unlock user storage, even user is not started yet
storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
} catch (RemoteException | RuntimeException e) {
Slog.w(TAG, "Failed to unlock: " + e.getMessage());
}
}
synchronized (mLock) {
// Register the given listener to watch for unlock progress
uss = mStartedUsers.get(userId);
if (uss != null) {
uss.mUnlockProgress.addListener(listener);
uss.tokenProvided = (token != null);
}
}
// Bail if user isn't actually running
if (uss == null) {
notifyFinished(userId, listener);
return false;
}
finishUserUnlocking(uss);
// We just unlocked a user, so let's now attempt to unlock any
// managed 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) {
Slog.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);
int currentUserId = getCurrentUserId();
UserInfo targetUserInfo = getUserInfo(targetUserId);
if (targetUserId == currentUserId) {
Slog.i(TAG, "user #" + targetUserId + " is already the current user");
return true;
}
if (targetUserInfo == null) {
Slog.w(TAG, "No user info for user #" + targetUserId);
return false;
}
if (!targetUserInfo.supportsSwitchTo()) {
Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
return false;
}
if (targetUserInfo.isManagedProfile()) {
Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
return false;
}
synchronized (mLock) {
mTargetUserId = targetUserId;
}
if (mUserSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
mUiHandler.sendMessage(mHandler.obtainMessage(
START_USER_SWITCH_UI_MSG, userNames));
} else {
mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
START_USER_SWITCH_FG_MSG, targetUserId, 0));
}
return true;
}
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,
getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage());
}
private void dispatchForegroundProfileChanged(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 */
void dispatchUserSwitchComplete(int userId) {
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
} catch (RemoteException e) {
}
}
mUserSwitchObservers.finishBroadcast();
}
private void dispatchLockedBootComplete(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 stopBackgroundUsersIfEnforced(int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
return;
}
// For now, only check for user restriction. Additional checks can be added here
boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND,
oldUserId);
if (!disallowRunInBg) {
return;
}
synchronized (mLock) {
if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
+ " and related users");
stopUsersLU(oldUserId, false, null);
}
}
private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mLock) {
Slog.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);
}
}
private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
synchronized (mLock) {
if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+ ". Observers that didn't respond: " + mTimeoutUserSwitchCallbacks);
mTimeoutUserSwitchCallbacks = null;
}
}
}
void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
Slog.d(TAG, "Dispatch onUserSwitching oldUser #" + oldUserId + " newUser #" + newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
if (observerCount > 0) {
final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
synchronized (mLock) {
uss.switching = true;
mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
}
final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
final long dispatchStartedTime = SystemClock.elapsedRealtime();
for (int i = 0; i < observerCount; i++) {
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 {
synchronized (mLock) {
long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
if (delay > USER_SWITCH_TIMEOUT_MS) {
Slog.e(TAG, "User switch timeout: observer " + name
+ " sent result after " + delay + " ms");
}
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);
}
}
}
};
mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
} catch (RemoteException e) {
}
}
} else {
synchronized (mLock) {
sendContinueUserSwitchLU(uss, oldUserId, newUserId);
}
}
mUserSwitchObservers.finishBroadcast();
}
void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
}
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
if (mUserSwitchUiEnabled) {
mInjector.getWindowManager().stopFreezingScreen();
}
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG,
newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
stopBackgroundUsersIfEnforced(oldUserId);
}
private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
boolean homeInFront = mInjector.stackSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivity(newUserId, "moveUserToForeground");
} else {
mInjector.stackSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
sendUserSwitchBroadcasts(oldUserId, newUserId);
}
void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
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);
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, 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);
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, 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);
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,
UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
int handleIncomingUser(int callingPid, int callingUid, 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;
if (mInjector.isCallerRecents(callingUid)
&& callingUserId == getCurrentUserId()
&& isSameProfileGroup(callingUserId, targetUserId)) {
// If the caller is Recents and it is running in the current user, 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 (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) {
// 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(callingUserId, targetUserId);
} 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 user ");
builder.append(UserHandle.getUserId(callingUid));
builder.append("; this requires ");
builder.append(INTERACT_ACROSS_USERS_FULL);
if (allowMode != ALLOW_FULL_ONLY) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
}
String msg = builder.toString();
Slog.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;
}
int unsafeConvertIncomingUser(int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
? getCurrentUserId(): userId;
}
void ensureNotSpecialUser(int userId) {
if (userId >= 0) {
return;
}
throw new IllegalArgumentException("Call does not support special user #" + userId);
}
void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
Preconditions.checkNotNull(name, "Observer name cannot be null");
if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: registerUserSwitchObserver() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + INTERACT_ACROSS_USERS_FULL;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
mUserSwitchObservers.register(observer, name);
}
void sendForegroundProfileChanged(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(int userId) {
synchronized (mLock) {
return mStartedUsers.get(userId);
}
}
boolean hasStartedUserState(int userId) {
return mStartedUsers.get(userId) != null;
}
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);
}
}
}
void sendBootCompleted(IIntentReceiver resultTo) {
// Get a copy of mStartedUsers to use outside of lock
SparseArray<UserState> startedUsers;
synchronized (mLock) {
startedUsers = mStartedUsers.clone();
}
for (int i = 0; i < startedUsers.size(); i++) {
UserState uss = startedUsers.valueAt(i);
finishUserBoot(uss, resultTo);
}
}
void onSystemReady() {
updateCurrentProfileIds();
mInjector.reportCurWakefulnessUsageEvent();
}
/**
* Refreshes the list of users related to the current user when either a
* user switch happens or when a new related user is started in the
* background.
*/
private void updateCurrentProfileIds() {
final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(),
false /* enabledOnly */);
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(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 key
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
return StorageManager.isUserKeyUnlocked(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 key
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
return StorageManager.isUserKeyUnlocked(userId);
default:
return false;
}
}
return state.state != UserState.STATE_STOPPING && state.state != UserState.STATE_SHUTDOWN;
}
UserInfo getCurrentUser() {
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;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// Optimization - if there is no pending user switch, return current id
if (mTargetUserId == UserHandle.USER_NULL) {
return getUserInfo(mCurrentUserId);
}
synchronized (mLock) {
return getCurrentUserLU();
}
}
UserInfo getCurrentUserLU() {
int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
return getUserInfo(userId);
}
int getCurrentOrTargetUserId() {
synchronized (mLock) {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
}
int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
int getCurrentUserIdLU() {
return mCurrentUserId;
}
int getCurrentUserId() {
synchronized (mLock) {
return mCurrentUserId;
}
}
private boolean isCurrentUserLU(int userId) {
return userId == getCurrentOrTargetUserIdLU();
}
int[] getUsers() {
UserManagerService ums = mInjector.getUserManager();
return ums != null ? ums.getUserIds() : new int[] { 0 };
}
UserInfo getUserInfo(int userId) {
return mInjector.getUserManager().getUserInfo(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(int userId) {
if (userId != UserHandle.USER_ALL) {
return new int[] {userId};
} else {
return getUsers();
}
}
boolean exists(int userId) {
return mInjector.getUserManager().exists(userId);
}
void enforceShellRestriction(String restriction, int userHandle) {
if (Binder.getCallingUid() == SHELL_UID) {
if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) {
throw new SecurityException("Shell does not have permission to access user "
+ userHandle);
}
}
}
boolean hasUserRestriction(String restriction, int userId) {
return mInjector.getUserManager().hasUserRestriction(restriction, userId);
}
Set<Integer> getProfileIds(int userId) {
Set<Integer> userIds = new HashSet<>();
final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(userId,
false /* enabledOnly */);
for (UserInfo user : profiles) {
userIds.add(user.id);
}
return userIds;
}
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(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(int userId) {
synchronized (mLock) {
return ArrayUtils.contains(mCurrentProfileIds, userId);
}
}
int[] getCurrentProfileIds() {
synchronized (mLock) {
return mCurrentProfileIds;
}
}
void onUserRemoved(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 work apps when the Work Challenge is present.
*/
protected boolean shouldConfirmCredentials(int userId) {
synchronized (mLock) {
if (mStartedUsers.get(userId) == null) {
return false;
}
}
if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
return false;
}
final KeyguardManager km = mInjector.getKeyguardManager();
return km.isDeviceLocked(userId) && km.isDeviceSecure(userId);
}
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;
}
}
private String getSwitchingFromSystemUserMessage() {
synchronized (mLock) {
return mSwitchingFromSystemUserMessage;
}
}
private String getSwitchingToSystemUserMessage() {
synchronized (mLock) {
return mSwitchingToSystemUserMessage;
}
}
void writeToProto(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.writeToProto(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.end(token);
}
}
void dump(PrintWriter pw, boolean dumpAll) {
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));
}
}
}
}
public boolean handleMessage(Message msg) {
switch (msg.what) {
case START_USER_SWITCH_FG_MSG:
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 SYSTEM_USER_START_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
mInjector.getSystemServiceManager().startUser(msg.arg1);
break;
case SYSTEM_USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
// Loads recents on a worker thread that allows disk I/O
FgThread.getHandler().post(() -> {
mInjector.loadUserRecents(userId);
});
finishUserUnlocked((UserState) msg.obj);
break;
case SYSTEM_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().switchUser(msg.arg1);
break;
case FOREGROUND_PROFILE_CHANGED_MSG:
dispatchForegroundProfileChanged(msg.arg1);
break;
case REPORT_USER_SWITCH_COMPLETE_MSG:
dispatchUserSwitchComplete(msg.arg1);
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
dispatchLockedBootComplete(msg.arg1);
break;
case START_USER_SWITCH_UI_MSG:
showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj);
break;
}
return false;
}
private static class UserProgressListener extends IProgressListener.Stub {
private volatile long mUnlockStarted;
@Override
public void onStarted(int id, Bundle extras) throws RemoteException {
Slog.d(TAG, "Started unlocking user " + id);
mUnlockStarted = SystemClock.uptimeMillis();
}
@Override
public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
Slog.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 TimingsTraceLog("SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER)
.logDuration("SystemUserUnlock", unlockTime);
} else {
Slog.d(TAG, "Unlocking user " + id + " took " + unlockTime + " ms");
}
}
};
@VisibleForTesting
static class Injector {
private final ActivityManagerService mService;
private UserManagerService mUserManager;
private UserManagerInternal mUserManagerInternal;
Injector(ActivityManagerService service) {
mService = service;
}
protected Handler getHandler(Handler.Callback callback) {
return new Handler(mService.mHandlerThread.getLooper(), callback);
}
protected Handler getUiHandler(Handler.Callback callback) {
return new Handler(mService.mUiHandler.getLooper(), callback);
}
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 userId) {
// TODO b/64165549 Verify that mLock is not held before calling AMS methods
synchronized (mService) {
return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
ordered, sticky, callingPid, callingUid, userId);
}
}
int checkCallingPermission(String permission) {
return mService.checkCallingPermission(permission);
}
WindowManagerService getWindowManager() {
return mService.mWindowManager;
}
void activityManagerOnUserStopped(int userId) {
synchronized (mService) {
mService.onUserStoppedLocked(userId);
}
}
void systemServiceManagerCleanupUser(int userId) {
mService.mSystemServiceManager.cleanupUser(userId);
}
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 mService.mSystemServiceManager.isRuntimeRestarted();
}
SystemServiceManager getSystemServiceManager() {
return mService.mSystemServiceManager;
}
boolean isFirstBootOrUpgrade() {
IPackageManager pm = AppGlobals.getPackageManager();
try {
return pm.isFirstBoot() || pm.isUpgrade();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
void sendPreBootBroadcast(int userId, boolean quiet, final Runnable onFinish) {
new PreBootBroadcaster(mService, userId, null, quiet) {
@Override
public void onFinished() {
onFinish.run();
}
}.sendNext();
}
void activityManagerForceStopPackage(int userId, String reason) {
synchronized (mService) {
mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
userId, reason);
}
};
int checkComponentPermission(String permission, int pid, int uid, int owningUid,
boolean exported) {
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
protected void startHomeActivity(int userId, String reason) {
synchronized (mService) {
mService.startHomeActivityLocked(userId, reason);
}
}
void updateUserConfiguration() {
synchronized (mService) {
mService.updateUserConfigurationLocked();
}
}
void clearBroadcastQueueForUser(int userId) {
synchronized (mService) {
mService.clearBroadcastQueueForUserLocked(userId);
}
}
void loadUserRecents(int userId) {
synchronized (mService) {
mService.getRecentTasks().loadUserRecentsLocked(userId);
}
}
void startPersistentApps(int matchFlags) {
mService.startPersistentApps(matchFlags);
}
void installEncryptionUnawareProviders(int userId) {
mService.installEncryptionUnawareProviders(userId);
}
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser,
true /* above system */, switchingFromSystemUserMessage,
switchingToSystemUserMessage);
d.show();
}
void reportGlobalUsageEventLocked(int event) {
synchronized (mService) {
mService.reportGlobalUsageEventLocked(event);
}
}
void reportCurWakefulnessUsageEvent() {
synchronized (mService) {
mService.reportCurWakefulnessUsageEventLocked();
}
}
void stackSupervisorRemoveUser(int userId) {
synchronized (mService) {
mService.mStackSupervisor.removeUserLocked(userId);
}
}
protected boolean stackSupervisorSwitchUser(int userId, UserState uss) {
synchronized (mService) {
return mService.mStackSupervisor.switchUserLocked(userId, uss);
}
}
protected void stackSupervisorResumeFocusedStackTopActivity() {
synchronized (mService) {
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
protected void clearAllLockedTasks(String reason) {
synchronized (mService) {
mService.getLockTaskController().clearLockedTasks(reason);
}
}
protected boolean isCallerRecents(int callingUid) {
return mService.getRecentTasks().isCallerRecents(callingUid);
}
}
}