| /* | |
| * 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.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.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 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.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.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; | |
| /** | |
| * 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); | |
| } | |
| } | |
| void stopRunningUsersLU(int maxRunningUsers) { | |
| int currentlyRunning = mUserLru.size(); | |
| int i = 0; | |
| while (currentlyRunning > maxRunningUsers && i < mUserLru.size()) { | |
| Integer oldUserId = mUserLru.get(i); | |
| UserState oldUss = mStartedUsers.get(oldUserId); | |
| if (oldUss == null) { | |
| // Shouldn't happen, but be sane if it does. | |
| mUserLru.remove(i); | |
| currentlyRunning--; | |
| continue; | |
| } | |
| if (oldUss.state == UserState.STATE_STOPPING | |
| || oldUss.state == UserState.STATE_SHUTDOWN) { | |
| // This user is already stopping, doesn't count. | |
| currentlyRunning--; | |
| i++; | |
| continue; | |
| } | |
| if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) { | |
| // Owner/System user and current user can't be stopped. We count it as running | |
| // when it is not a pure system user. | |
| if (UserInfo.isSystemOnly(oldUserId)) { | |
| currentlyRunning--; | |
| } | |
| i++; | |
| continue; | |
| } | |
| // This is a user to be stopped. | |
| if (stopUsersLU(oldUserId, false, null) == USER_OP_SUCCESS) { | |
| currentlyRunning--; | |
| } | |
| i++; | |
| } | |
| } | |
| 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) { | |
| // 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_LOCKED, STATE_RUNNING_UNLOCKING)) { | |
| return; | |
| } | |
| } | |
| mInjector.getUserManagerInternal().setUserState(userId, uss.state); | |
| uss.mUnlockProgress.start(); | |
| // Prepare app storage before we go any further | |
| uss.mUnlockProgress.setProgress(5, | |
| mInjector.getContext().getString(R.string.android_start_title)); | |
| mInjector.getUserManager().onBeforeUnlockUser(userId); | |
| 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(); | |
| // 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().removeUser(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() { | |
| IntArray userIds = new IntArray(); | |
| synchronized (mLock) { | |
| final int num = mUserLru.size(); | |
| for (int i = 0; i < num; i++) { | |
| Integer oldUserId = mUserLru.get(i); | |
| UserState oldUss = mStartedUsers.get(oldUserId); | |
| if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId | |
| || oldUss.state == UserState.STATE_STOPPING | |
| || oldUss.state == UserState.STATE_SHUTDOWN) { | |
| continue; | |
| } | |
| userIds.add(oldUserId); | |
| } | |
| } | |
| final int userIdsSize = userIds.size(); | |
| for (int i = 0; i < userIdsSize; i++) { | |
| int oldUserId = userIds.get(i); | |
| 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); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| 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")); | |
| } | |
| /** | |
| * 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) { | |
| 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) { | |
| // TODO: I don't think this does what the caller think it does. Seems to only | |
| // remove one locked task and won't work if multiple locked tasks are present. | |
| mInjector.clearLockTaskMode("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 (updateUmState) { | |
| mInjector.getUserManagerInternal().setUserState(userId, uss.state); | |
| } | |
| if (foreground) { | |
| synchronized (mLock) { | |
| mCurrentUserId = userId; | |
| mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up | |
| } | |
| mInjector.updateUserConfiguration(); | |
| updateCurrentProfileIds(); | |
| mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds()); | |
| // 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.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) { | |
| Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId | |
| + " when device is in demo mode"); | |
| 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); | |
| } | |
| 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(); | |
| 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.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 && targetUserId < 0) { | |
| throw new IllegalArgumentException( | |
| "Call does not support special user #" + 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 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(); | |
| } | |
| /** | |
| * 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(); | |
| } | |
| 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 isCurrentProfile(int userId) { | |
| synchronized (mLock) { | |
| return ArrayUtils.contains(mCurrentProfileIds, userId); | |
| } | |
| } | |
| int[] getCurrentProfileIds() { | |
| synchronized (mLock) { | |
| return mCurrentProfileIds; | |
| } | |
| } | |
| /** | |
| * 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 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 (dumpAll) { | |
| pw.print(" mStartedUserArray: "); | |
| pw.println(Arrays.toString(mStartedUserArray)); | |
| } | |
| 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); | |
| mInjector.loadUserRecents(userId); | |
| if (userId == UserHandle.USER_SYSTEM) { | |
| mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE); | |
| } | |
| mInjector.installEncryptionUnawareProviders(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) { | |
| Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser, | |
| true /* above system */); | |
| d.show(); | |
| } | |
| 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 clearLockTaskMode(String reason) { | |
| synchronized (mService) { | |
| mService.mLockTaskController.clearLockTaskMode(reason); | |
| } | |
| } | |
| } | |
| } |