Merge "Introduce config for auto-created guest users"
This commit is contained in:
committed by
Android (Google) Code Review
commit
7bec4b3029
@@ -3529,6 +3529,12 @@
|
||||
<!-- If true, all guest users created on the device will be ephemeral. -->
|
||||
<bool name="config_guestUserEphemeral">false</bool>
|
||||
|
||||
<!-- Whether device should always have a guest user available. If true, guest user will be
|
||||
created on boot, and a new guest user will be created in the background anytime the current
|
||||
guest user is removed. Instead of showing "Add guest" and "Remove guest", the UI will show
|
||||
"Guest" and "Reset guest". -->
|
||||
<bool name="config_guestUserAutoCreated">false</bool>
|
||||
|
||||
<!-- Enforce strong auth on boot. Setting this to false represents a security risk and should
|
||||
not be ordinarily done. The only case in which this might be permissible is in a car head
|
||||
unit where there are hardware mechanisms to protect the device (physical keys) and not
|
||||
|
||||
@@ -402,6 +402,7 @@
|
||||
<java-symbol type="bool" name="config_supportsSystemDecorsOnSecondaryDisplays" />
|
||||
<java-symbol type="bool" name="config_supportsInsecureLockScreen" />
|
||||
<java-symbol type="bool" name="config_guestUserEphemeral" />
|
||||
<java-symbol type="bool" name="config_guestUserAutoCreated" />
|
||||
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
|
||||
<java-symbol type="array" name="config_localPrivateDisplayPorts" />
|
||||
<java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
|
||||
|
||||
@@ -1422,6 +1422,8 @@
|
||||
<string name="guest_new_guest">Add guest</string>
|
||||
<!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
|
||||
<string name="guest_exit_guest">Remove guest</string>
|
||||
<!-- Label for resetting guest session in the user switcher, which will remove all data from the current guest session [CHAR LIMIT=35] -->
|
||||
<string name="guest_reset_guest">Reset guest</string>
|
||||
<!-- Name for the guest user [CHAR LIMIT=35] -->
|
||||
<string name="guest_nickname">Guest</string>
|
||||
|
||||
|
||||
@@ -1148,12 +1148,18 @@
|
||||
<!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
|
||||
<string name="guest_exit_guest_dialog_title">Remove guest?</string>
|
||||
|
||||
<!-- Title of the confirmation dialog when resetting guest session [CHAR LIMIT=NONE] -->
|
||||
<string name="guest_reset_guest_dialog_title">Reset guest?</string>
|
||||
|
||||
<!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
|
||||
<string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string>
|
||||
|
||||
<!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] -->
|
||||
<string name="guest_exit_guest_dialog_remove">Remove</string>
|
||||
|
||||
<!-- Label for button in confirmation dialog when resetting guest session [CHAR LIMIT=35] -->
|
||||
<string name="guest_reset_guest_dialog_remove">Reset</string>
|
||||
|
||||
<!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
|
||||
<string name="guest_wipe_session_title">Welcome back, guest!</string>
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -24,11 +23,8 @@ import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
@@ -36,6 +32,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.qs.QSUserSwitcherEvent;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
/**
|
||||
@@ -51,11 +48,14 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
@VisibleForTesting
|
||||
public AlertDialog mNewSessionDialog;
|
||||
private final UserTracker mUserTracker;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final SecureSettings mSecureSettings;
|
||||
|
||||
public GuestResumeSessionReceiver(UserTracker userTracker, UiEventLogger uiEventLogger,
|
||||
public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
|
||||
UserTracker userTracker, UiEventLogger uiEventLogger,
|
||||
SecureSettings secureSettings) {
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mUserTracker = userTracker;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mSecureSettings = secureSettings;
|
||||
@@ -92,8 +92,8 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
int notFirstLogin = mSecureSettings.getIntForUser(
|
||||
SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
|
||||
if (notFirstLogin != 0) {
|
||||
mNewSessionDialog = new ResetSessionDialog(context, mUserTracker, mUiEventLogger,
|
||||
userId);
|
||||
mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
|
||||
mUserTracker, mUiEventLogger, userId);
|
||||
mNewSessionDialog.show();
|
||||
} else {
|
||||
mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
|
||||
@@ -101,48 +101,6 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipes the guest session.
|
||||
*
|
||||
* The guest must be the current user and its id must be {@param userId}.
|
||||
*/
|
||||
private static void wipeGuestSession(Context context, UserTracker userTracker, int userId) {
|
||||
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
UserInfo currentUser = userTracker.getUserInfo();
|
||||
if (currentUser.id != userId) {
|
||||
Log.w(TAG, "User requesting to start a new session (" + userId + ")"
|
||||
+ " is not current user (" + currentUser.id + ")");
|
||||
return;
|
||||
}
|
||||
if (!currentUser.isGuest()) {
|
||||
Log.w(TAG, "User requesting to start a new session (" + userId + ")"
|
||||
+ " is not a guest");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean marked = userManager.markGuestForDeletion(currentUser.id);
|
||||
if (!marked) {
|
||||
Log.w(TAG, "Couldn't mark the guest for deletion for user " + userId);
|
||||
return;
|
||||
}
|
||||
UserInfo newGuest = userManager.createGuest(context, currentUser.name);
|
||||
|
||||
try {
|
||||
if (newGuest == null) {
|
||||
Log.e(TAG, "Could not create new guest, switching back to system user");
|
||||
ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
|
||||
userManager.removeUser(currentUser.id);
|
||||
WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */);
|
||||
return;
|
||||
}
|
||||
ActivityManager.getService().switchUser(newGuest.id);
|
||||
userManager.removeUser(currentUser.id);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelDialog() {
|
||||
if (mNewSessionDialog != null && mNewSessionDialog.isShowing()) {
|
||||
mNewSessionDialog.cancel();
|
||||
@@ -162,11 +120,14 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
@VisibleForTesting
|
||||
public static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
|
||||
|
||||
private final UserTracker mUserTracker;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final int mUserId;
|
||||
|
||||
ResetSessionDialog(Context context, UserTracker userTracker, UiEventLogger uiEventLogger,
|
||||
ResetSessionDialog(Context context,
|
||||
UserSwitcherController userSwitcherController,
|
||||
UserTracker userTracker,
|
||||
UiEventLogger uiEventLogger,
|
||||
int userId) {
|
||||
super(context);
|
||||
|
||||
@@ -179,7 +140,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
setButton(BUTTON_DONTWIPE,
|
||||
context.getString(R.string.guest_wipe_session_dontwipe), this);
|
||||
|
||||
mUserTracker = userTracker;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mUserId = userId;
|
||||
}
|
||||
@@ -188,7 +149,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == BUTTON_WIPE) {
|
||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_WIPE);
|
||||
wipeGuestSession(getContext(), mUserTracker, mUserId);
|
||||
mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
|
||||
dismiss();
|
||||
} else if (which == BUTTON_DONTWIPE) {
|
||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE);
|
||||
|
||||
@@ -119,6 +119,7 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -257,6 +258,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
/** TrustManager for letting it know when we change visibility */
|
||||
private final TrustManager mTrustManager;
|
||||
|
||||
/** UserSwitcherController for creating guest user on boot complete */
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
|
||||
/**
|
||||
* Used to keep the device awake while to ensure the keyguard finishes opening before
|
||||
* we sleep.
|
||||
@@ -805,6 +809,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
|
||||
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
|
||||
TrustManager trustManager,
|
||||
UserSwitcherController userSwitcherController,
|
||||
DeviceConfigProxy deviceConfig,
|
||||
NavigationModeController navigationModeController,
|
||||
KeyguardDisplayManager keyguardDisplayManager,
|
||||
@@ -825,6 +830,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
mUpdateMonitor = keyguardUpdateMonitor;
|
||||
mPM = powerManager;
|
||||
mTrustManager = trustManager;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mKeyguardDisplayManager = keyguardDisplayManager;
|
||||
dumpManager.registerDumpable(getClass().getName(), this);
|
||||
mDeviceConfig = deviceConfig;
|
||||
@@ -2558,6 +2564,11 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
||||
@Override
|
||||
public void onBootCompleted() {
|
||||
synchronized (this) {
|
||||
if (mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_guestUserAutoCreated)) {
|
||||
// TODO(b/191067027): Move post-boot guest creation to system_server
|
||||
mUserSwitcherController.guaranteeGuestPresent();
|
||||
}
|
||||
mBootCompleted = true;
|
||||
adjustStatusBarLocked(false, true);
|
||||
if (mBootSendUserPresent) {
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.android.systemui.statusbar.phone.KeyguardLiftController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||
import com.android.systemui.util.settings.GlobalSettings;
|
||||
@@ -92,6 +93,7 @@ public class KeyguardModule {
|
||||
DumpManager dumpManager,
|
||||
PowerManager powerManager,
|
||||
TrustManager trustManager,
|
||||
UserSwitcherController userSwitcherController,
|
||||
@UiBackground Executor uiBgExecutor,
|
||||
DeviceConfigProxy deviceConfig,
|
||||
NavigationModeController navigationModeController,
|
||||
@@ -114,6 +116,7 @@ public class KeyguardModule {
|
||||
uiBgExecutor,
|
||||
powerManager,
|
||||
trustManager,
|
||||
userSwitcherController,
|
||||
deviceConfig,
|
||||
navigationModeController,
|
||||
keyguardDisplayManager,
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
|
||||
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -49,6 +50,7 @@ import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -64,6 +66,7 @@ import com.android.systemui.SystemUISecondaryUserService;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.qs.DetailAdapter;
|
||||
import com.android.systemui.qs.QSUserSwitcherEvent;
|
||||
@@ -79,6 +82,8 @@ import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
@@ -125,21 +130,31 @@ public class UserSwitcherController implements Dumpable {
|
||||
// When false, there won't be any visual affordance to add a new user from the keyguard even if
|
||||
// the user is unlocked
|
||||
private boolean mAddUsersFromLockScreen;
|
||||
private boolean mPauseRefreshUsers;
|
||||
@VisibleForTesting
|
||||
boolean mPauseRefreshUsers;
|
||||
private int mSecondaryUser = UserHandle.USER_NULL;
|
||||
private Intent mSecondaryUserServiceIntent;
|
||||
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
public final DetailAdapter mUserDetailAdapter;
|
||||
private final Executor mUiBgExecutor;
|
||||
private final boolean mGuestUserAutoCreated;
|
||||
private final AtomicBoolean mGuestCreationScheduled;
|
||||
|
||||
@Inject
|
||||
public UserSwitcherController(Context context, UserManager userManager, UserTracker userTracker,
|
||||
public UserSwitcherController(Context context,
|
||||
UserManager userManager,
|
||||
UserTracker userTracker,
|
||||
KeyguardStateController keyguardStateController,
|
||||
@Main Handler handler, ActivityStarter activityStarter,
|
||||
BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger,
|
||||
@Main Handler handler,
|
||||
ActivityStarter activityStarter,
|
||||
BroadcastDispatcher broadcastDispatcher,
|
||||
UiEventLogger uiEventLogger,
|
||||
TelephonyListenerManager telephonyListenerManager,
|
||||
IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter,
|
||||
SecureSettings secureSettings) {
|
||||
IActivityTaskManager activityTaskManager,
|
||||
UserDetailAdapter userDetailAdapter,
|
||||
SecureSettings secureSettings,
|
||||
@UiBackground Executor uiBgExecutor) {
|
||||
mContext = context;
|
||||
mUserTracker = userTracker;
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
@@ -147,11 +162,15 @@ public class UserSwitcherController implements Dumpable {
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
|
||||
mUserTracker, mUiEventLogger, secureSettings);
|
||||
this, mUserTracker, mUiEventLogger, secureSettings);
|
||||
mUserDetailAdapter = userDetailAdapter;
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
if (!UserManager.isGuestUserEphemeral()) {
|
||||
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
|
||||
}
|
||||
mGuestUserAutoCreated = mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_guestUserAutoCreated);
|
||||
mGuestCreationScheduled = new AtomicBoolean();
|
||||
mKeyguardStateController = keyguardStateController;
|
||||
mHandler = handler;
|
||||
mActivityStarter = activityStarter;
|
||||
@@ -400,21 +419,13 @@ public class UserSwitcherController implements Dumpable {
|
||||
int id;
|
||||
if (record.isGuest && record.info == null) {
|
||||
// No guest user. Create one.
|
||||
UserInfo guest;
|
||||
try {
|
||||
guest = mUserManager.createGuest(mContext,
|
||||
mContext.getString(com.android.settingslib.R.string.guest_nickname));
|
||||
} catch (UserManager.UserOperationException e) {
|
||||
Log.e(TAG, "Couldn't create guest user", e);
|
||||
return;
|
||||
}
|
||||
if (guest == null) {
|
||||
// Couldn't create guest, most likely because there already exists one, we just
|
||||
// haven't reloaded the user list yet.
|
||||
int guestId = createGuest();
|
||||
if (guestId == UserHandle.USER_NULL) {
|
||||
// This may happen if we haven't reloaded the user list yet.
|
||||
return;
|
||||
}
|
||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
|
||||
id = guest.id;
|
||||
id = guestId;
|
||||
} else if (record.isAddUser) {
|
||||
showAddUserDialog();
|
||||
return;
|
||||
@@ -478,11 +489,6 @@ public class UserSwitcherController implements Dumpable {
|
||||
mAddUserDialog.show();
|
||||
}
|
||||
|
||||
protected void exitGuest(int id, int targetId) {
|
||||
switchToUserId(targetId);
|
||||
mUserManager.removeUser(id);
|
||||
}
|
||||
|
||||
private void listenForCallState() {
|
||||
mTelephonyListenerManager.addCallStateListener(mPhoneStateListener);
|
||||
}
|
||||
@@ -588,6 +594,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
pw.print(" "); pw.println(u.toString());
|
||||
}
|
||||
pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher);
|
||||
pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated);
|
||||
}
|
||||
|
||||
/** Returns the name of the current user of the phone. */
|
||||
@@ -614,6 +621,120 @@ public class UserSwitcherController implements Dumpable {
|
||||
return mUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes guest user and switches to target user. The guest must be the current user and its id
|
||||
* must be {@code guestUserId}.
|
||||
*
|
||||
* <p>If {@code targetUserId} is {@link UserHandle.USER_NULL}, then create a new guest user in
|
||||
* the foreground, and immediately switch to it. This is used for wiping the current guest and
|
||||
* replacing it with a new one.
|
||||
*
|
||||
* <p>If {@code targetUserId} is specified, then remove the guest in the background while
|
||||
* switching to {@code targetUserId}.
|
||||
*
|
||||
* <p>If device is configured with {@link
|
||||
* com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
|
||||
* new one is created in the background. This has no effect if {@code targetUserId} is {@link
|
||||
* UserHandle.USER_NULL}.
|
||||
*
|
||||
* @param guestUserId id of the guest user to remove
|
||||
* @param targetUserId id of the user to switch to after guest is removed. If {@link
|
||||
* UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
|
||||
*/
|
||||
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
|
||||
UserInfo currentUser = mUserTracker.getUserInfo();
|
||||
if (currentUser.id != guestUserId) {
|
||||
Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
|
||||
+ " is not current user (" + currentUser.id + ")");
|
||||
return;
|
||||
}
|
||||
if (!currentUser.isGuest()) {
|
||||
Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
|
||||
+ " is not a guest");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean marked = mUserManager.markGuestForDeletion(currentUser.id);
|
||||
if (!marked) {
|
||||
Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (targetUserId == UserHandle.USER_NULL) {
|
||||
// Create a new guest in the foreground, and then immediately switch to it
|
||||
int newGuestId = createGuest();
|
||||
if (newGuestId == UserHandle.USER_NULL) {
|
||||
Log.e(TAG, "Could not create new guest, switching back to system user");
|
||||
switchToUserId(UserHandle.USER_SYSTEM);
|
||||
mUserManager.removeUser(currentUser.id);
|
||||
WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null);
|
||||
return;
|
||||
}
|
||||
switchToUserId(newGuestId);
|
||||
mUserManager.removeUser(currentUser.id);
|
||||
} else {
|
||||
if (mGuestUserAutoCreated) {
|
||||
// TODO(b/191067027): Move guest recreation to system_server
|
||||
scheduleGuestCreation();
|
||||
}
|
||||
switchToUserId(targetUserId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Couldn't remove guest because ActivityManager or WindowManager is dead");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleGuestCreation() {
|
||||
if (!mGuestCreationScheduled.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mUiBgExecutor.execute(() -> {
|
||||
int newGuestId = createGuest();
|
||||
if (newGuestId == UserHandle.USER_NULL) {
|
||||
Log.w(TAG, "Could not create new guest while exiting existing guest");
|
||||
}
|
||||
mGuestCreationScheduled.set(false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no guest on the device, schedule creation of a new guest user in the background.
|
||||
*/
|
||||
public void guaranteeGuestPresent() {
|
||||
if (mUserManager.findCurrentGuestUser() == null) {
|
||||
scheduleGuestCreation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a guest user and return its multi-user user ID.
|
||||
*
|
||||
* This method does not check if a guest already exists before it makes a call to
|
||||
* {@link UserManager} to create a new one.
|
||||
*
|
||||
* @return The multi-user user ID of the newly created guest user, or
|
||||
* {@link UserHandle.USER_NULL} if the guest couldn't be created.
|
||||
*/
|
||||
public @UserIdInt int createGuest() {
|
||||
UserInfo guest;
|
||||
try {
|
||||
guest = mUserManager.createGuest(mContext,
|
||||
mContext.getString(com.android.settingslib.R.string.guest_nickname));
|
||||
} catch (UserManager.UserOperationException e) {
|
||||
Log.e(TAG, "Couldn't create guest user", e);
|
||||
return UserHandle.USER_NULL;
|
||||
}
|
||||
if (guest == null) {
|
||||
Log.e(TAG, "Couldn't create guest, most likely because there already exists one");
|
||||
return UserHandle.USER_NULL;
|
||||
}
|
||||
return guest.id;
|
||||
}
|
||||
|
||||
public static abstract class BaseUserAdapter extends BaseAdapter {
|
||||
|
||||
final UserSwitcherController mController;
|
||||
@@ -674,10 +795,15 @@ public class UserSwitcherController implements Dumpable {
|
||||
public String getName(Context context, UserRecord item) {
|
||||
if (item.isGuest) {
|
||||
if (item.isCurrent) {
|
||||
return context.getString(com.android.settingslib.R.string.guest_exit_guest);
|
||||
return context.getString(mController.mGuestUserAutoCreated
|
||||
? com.android.settingslib.R.string.guest_reset_guest
|
||||
: com.android.settingslib.R.string.guest_exit_guest);
|
||||
} else {
|
||||
// If config_guestUserAutoCreated, always show guest nickname instead of "Add
|
||||
// guest" to make it seem as though the device always has a guest ready for use
|
||||
return context.getString(
|
||||
item.info == null ? com.android.settingslib.R.string.guest_new_guest
|
||||
item.info == null && !mController.mGuestUserAutoCreated
|
||||
? com.android.settingslib.R.string.guest_new_guest
|
||||
: com.android.settingslib.R.string.guest_nickname);
|
||||
}
|
||||
} else if (item.isAddUser) {
|
||||
@@ -894,12 +1020,15 @@ public class UserSwitcherController implements Dumpable {
|
||||
|
||||
public ExitGuestDialog(Context context, int guestId, int targetId) {
|
||||
super(context);
|
||||
setTitle(R.string.guest_exit_guest_dialog_title);
|
||||
setTitle(mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_title
|
||||
: R.string.guest_exit_guest_dialog_title);
|
||||
setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
|
||||
setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||
context.getString(android.R.string.cancel), this);
|
||||
setButton(DialogInterface.BUTTON_POSITIVE,
|
||||
context.getString(R.string.guest_exit_guest_dialog_remove), this);
|
||||
context.getString(
|
||||
mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_remove
|
||||
: R.string.guest_exit_guest_dialog_remove), this);
|
||||
SystemUIDialog.setWindowOnTop(this);
|
||||
setCanceledOnTouchOutside(false);
|
||||
mGuestId = guestId;
|
||||
@@ -913,7 +1042,7 @@ public class UserSwitcherController implements Dumpable {
|
||||
} else {
|
||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
|
||||
dismiss();
|
||||
exitGuest(mGuestId, mTargetId);
|
||||
removeGuestUser(mGuestId, mTargetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.DeviceConfigProxyFake;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
@@ -78,6 +79,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
private @Mock DumpManager mDumpManager;
|
||||
private @Mock PowerManager mPowerManager;
|
||||
private @Mock TrustManager mTrustManager;
|
||||
private @Mock UserSwitcherController mUserSwitcherController;
|
||||
private @Mock NavigationModeController mNavigationModeController;
|
||||
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
|
||||
private @Mock DozeParameters mDozeParameters;
|
||||
@@ -100,13 +102,27 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
||||
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
|
||||
|
||||
mViewMediator = new KeyguardViewMediator(
|
||||
mContext, mFalsingCollector, mLockPatternUtils, mBroadcastDispatcher,
|
||||
mContext,
|
||||
mFalsingCollector,
|
||||
mLockPatternUtils,
|
||||
mBroadcastDispatcher,
|
||||
() -> mStatusBarKeyguardViewManager,
|
||||
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
|
||||
mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController,
|
||||
mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController,
|
||||
mKeyguardStateController, () -> mKeyguardUnlockAnimationController,
|
||||
mUnlockedScreenOffAnimationController, () -> mNotificationShadeDepthController);
|
||||
mDismissCallbackRegistry,
|
||||
mUpdateMonitor,
|
||||
mDumpManager,
|
||||
mUiBgExecutor,
|
||||
mPowerManager,
|
||||
mTrustManager,
|
||||
mUserSwitcherController,
|
||||
mDeviceConfig,
|
||||
mNavigationModeController,
|
||||
mKeyguardDisplayManager,
|
||||
mDozeParameters,
|
||||
mStatusBarStateController,
|
||||
mKeyguardStateController,
|
||||
() -> mKeyguardUnlockAnimationController,
|
||||
mUnlockedScreenOffAnimationController,
|
||||
() -> mNotificationShadeDepthController);
|
||||
mViewMediator.start();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.graphics.Bitmap
|
||||
import android.hardware.face.FaceManager
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.Handler
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
@@ -39,7 +40,9 @@ import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.qs.QSUserSwitcherEvent
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.telephony.TelephonyListenerManager
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.settings.SecureSettings
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
@@ -67,24 +70,37 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
@Mock private lateinit var activityTaskManager: IActivityTaskManager
|
||||
@Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
|
||||
@Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
|
||||
@Mock private lateinit var userInfo: UserInfo
|
||||
@Mock private lateinit var secureSettings: SecureSettings
|
||||
private lateinit var testableLooper: TestableLooper
|
||||
private lateinit var userSwitcherController: UserSwitcherController
|
||||
private lateinit var uiBgExecutor: FakeExecutor
|
||||
private lateinit var uiEventLogger: UiEventLoggerFake
|
||||
private lateinit var userSwitcherController: UserSwitcherController
|
||||
private lateinit var picture: Bitmap
|
||||
private val ownerId = UserHandle.USER_SYSTEM
|
||||
private val ownerInfo = UserInfo(ownerId, "Owner", null,
|
||||
UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL or UserInfo.FLAG_INITIALIZED or
|
||||
UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM,
|
||||
UserManager.USER_TYPE_FULL_SYSTEM)
|
||||
private val guestId = 1234
|
||||
private val guestInfo = UserInfo(guestId, "Guest", null,
|
||||
UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testableLooper = TestableLooper.get(this)
|
||||
uiBgExecutor = FakeExecutor(FakeSystemClock())
|
||||
uiEventLogger = UiEventLoggerFake()
|
||||
|
||||
context.orCreateTestableResources.addOverride(
|
||||
com.android.internal.R.bool.config_guestUserAutoCreated, false)
|
||||
|
||||
mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
|
||||
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
|
||||
mock(FingerprintManager::class.java))
|
||||
|
||||
`when`(userManager.canAddMoreUsers()).thenReturn(true)
|
||||
|
||||
userSwitcherController = UserSwitcherController(context,
|
||||
userManager,
|
||||
userTracker,
|
||||
@@ -96,7 +112,10 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
telephonyListenerManager,
|
||||
activityTaskManager,
|
||||
userDetailAdapter,
|
||||
secureSettings)
|
||||
secureSettings,
|
||||
uiBgExecutor)
|
||||
userSwitcherController.mPauseRefreshUsers = true
|
||||
|
||||
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
||||
}
|
||||
|
||||
@@ -110,8 +129,10 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
`when`(userTracker.userId).thenReturn(ownerId)
|
||||
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||
|
||||
`when`(userManager.createGuest(any(), anyString())).thenReturn(userInfo)
|
||||
`when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
|
||||
|
||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
|
||||
testableLooper.processAllMessages()
|
||||
@@ -122,13 +143,15 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testRemoveGuest_removeButtonPressed_isLogged() {
|
||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||
userInfo,
|
||||
guestInfo,
|
||||
picture,
|
||||
true /* guest */,
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||
@@ -142,13 +165,15 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
|
||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||
userInfo,
|
||||
guestInfo,
|
||||
picture,
|
||||
true /* guest */,
|
||||
true /* current */,
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||
@@ -160,7 +185,6 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testWipeGuest_startOverButtonPressed_isLogged() {
|
||||
val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
|
||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||
guestInfo,
|
||||
picture,
|
||||
@@ -169,6 +193,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
// Simulate that guest user has already logged in
|
||||
`when`(secureSettings.getIntForUser(
|
||||
@@ -179,7 +205,6 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
|
||||
// Simulate a user switch event
|
||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
|
||||
userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
|
||||
@@ -194,7 +219,6 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testWipeGuest_continueButtonPressed_isLogged() {
|
||||
val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
|
||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||
guestInfo,
|
||||
picture,
|
||||
@@ -203,6 +227,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
false /* isAddUser */,
|
||||
false /* isRestricted */,
|
||||
true /* isSwitchToEnabled */)
|
||||
`when`(userTracker.userId).thenReturn(guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
// Simulate that guest user has already logged in
|
||||
`when`(secureSettings.getIntForUser(
|
||||
@@ -213,7 +239,6 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
||||
|
||||
// Simulate a user switch event
|
||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||
|
||||
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
|
||||
userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
|
||||
|
||||
Reference in New Issue
Block a user