From 7f7ffe21bb3b76261e477d723ddbf96ac8db4bd3 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Tue, 1 Jun 2021 17:07:12 -0700 Subject: [PATCH] Introduce config for auto-created guest users Create a new frameworks config: config_guestUserAutoCreated. If true, - Device should always have a guest user available. - Instead of showing "Add guest", the UI will show "Guest", and instead of "Remove guest", there will be an option to "Reset guest" - Guest user is always created on boot if it does not already exist - New guest user is created any time current guest user is removed For now, these behaviors will be handled by System UI and Settings. In addition, a few changes were made to System UI to simplify the code. These changes may also affect devices running without the new config: - Move GuestResumeSessionReceiver.wipeGuestSession() to UserSwitcherController - New method: UserSwitcherController.createGuest() - Introduce dependency from KeyguardViewMediator to UserSwitcherController - Introduce dependency from GuestResumeSessionReceiver to UserSwitcherController Bug: 188542158 Test: With config_guestUserAutoCreated=true, remove all guest users using adb (`adb shell cmd user list -v --all` to find the user ids, then remove each guest user with `adb shell pm remove-user `), reboot device, check that there is a new guest on boot. Test: With config_guestUserAutoCreated=true, reboot device when there is already a guest user, confirm that guest user was not wiped after reboot is completed. Test: With config_guestUserAutoCreated=true, switch to guest user, select "Reset guest" from QS tile, select "Reset". Phone should switch back to last active user, and QS tile should now show "Guest" instead of "Add guest". Run `adb shell cmd user list -v --all` to confirm guest has a new user id. Test: With config_guestUserAutoCreated=false, confirm that "Add guest" and "Remove guest" features remain unchanged Change-Id: Ib1c8dd0a0796d95681167f51e12e3f4be21345af --- core/res/res/values/config.xml | 6 + core/res/res/values/symbols.xml | 1 + packages/SettingsLib/res/values/strings.xml | 2 + packages/SystemUI/res/values/strings.xml | 6 + .../systemui/GuestResumeSessionReceiver.java | 67 ++----- .../keyguard/KeyguardViewMediator.java | 11 ++ .../keyguard/dagger/KeyguardModule.java | 3 + .../policy/UserSwitcherController.java | 187 +++++++++++++++--- .../keyguard/KeyguardViewMediatorTest.java | 28 ++- .../policy/UserSwitcherControllerTest.kt | 45 ++++- 10 files changed, 258 insertions(+), 98 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 66aa828ef8c3b..8b406c141fac9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3529,6 +3529,12 @@ false + + false + Remove guest + + Reset guest Guest diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7d5144f9cc570..24acba5a3adff 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1148,12 +1148,18 @@ Remove guest? + + Reset guest? + All apps and data in this session will be deleted. Remove + + Reset + Welcome back, guest! diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 95c68255db837..1d355f22edd5e 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 327698392ac73..88e9f69620ed0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 05d1361c3ab83..8a383b974d77b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -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, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index e2129bdc9a057..fae4423a4277b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -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}. + * + *

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. + * + *

If {@code targetUserId} is specified, then remove the guest in the background while + * switching to {@code targetUserId}. + * + *

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); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d9b56a49f12a8..1dacc6245d841 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -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(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index e0f395d494463..019bd8d6320e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -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)