Add tests for guest user metrics

Test: statsd_testdrive -terse 90
Test: atest SystemUITests:com.android.systemui.qs.tiles.UserDetailViewAdapterTest
Test: atest SystemUITests:com.android.systemui.statusbar.policy.UserSwitcherControllerTest
Bug: 169783558
Change-Id: I009fc0eba32e34cb62f2cc15f9337bce9b81c89d
This commit is contained in:
Peter Kalauskas
2021-03-22 20:26:10 -07:00
parent 05b4d0d1dd
commit 83deb23a67
3 changed files with 294 additions and 56 deletions

View File

@@ -17,9 +17,8 @@
package com.android.systemui;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -28,14 +27,16 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
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.util.settings.SecureSettings;
/**
* Manages notification when a guest session is resumed.
@@ -44,13 +45,20 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
private static final String TAG = "GuestResumeSessionReceiver";
private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
@VisibleForTesting
public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
private Dialog mNewSessionDialog;
@VisibleForTesting
public AlertDialog mNewSessionDialog;
private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
private final SecureSettings mSecureSettings;
public GuestResumeSessionReceiver(UiEventLogger uiEventLogger) {
public GuestResumeSessionReceiver(UserTracker userTracker, UiEventLogger uiEventLogger,
SecureSettings secureSettings) {
mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
mSecureSettings = secureSettings;
}
/**
@@ -76,25 +84,19 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
return;
}
UserInfo currentUser;
try {
currentUser = ActivityManager.getService().getCurrentUser();
} catch (RemoteException e) {
return;
}
UserInfo currentUser = mUserTracker.getUserInfo();
if (!currentUser.isGuest()) {
return;
}
ContentResolver cr = context.getContentResolver();
int notFirstLogin = Settings.System.getIntForUser(
cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
int notFirstLogin = mSecureSettings.getIntForUser(
SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
if (notFirstLogin != 0) {
mNewSessionDialog = new ResetSessionDialog(context, mUiEventLogger, userId);
mNewSessionDialog = new ResetSessionDialog(context, mUserTracker, mUiEventLogger,
userId);
mNewSessionDialog.show();
} else {
Settings.System.putIntForUser(
cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
}
}
}
@@ -104,15 +106,9 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
*
* The guest must be the current user and its id must be {@param userId}.
*/
private static void wipeGuestSession(Context context, int userId) {
private static void wipeGuestSession(Context context, UserTracker userTracker, int userId) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
UserInfo currentUser;
try {
currentUser = ActivityManager.getService().getCurrentUser();
} catch (RemoteException e) {
Log.e(TAG, "Couldn't wipe session because ActivityManager is dead");
return;
}
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 + ")");
@@ -154,16 +150,24 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
}
}
private static class ResetSessionDialog extends SystemUIDialog implements
/**
* Dialog shown when user when asking for confirmation before deleting guest user.
*/
@VisibleForTesting
public static class ResetSessionDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
@VisibleForTesting
public static final int BUTTON_WIPE = BUTTON_NEGATIVE;
@VisibleForTesting
public static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
private final int mUserId;
ResetSessionDialog(Context context, UiEventLogger uiEventLogger, int userId) {
ResetSessionDialog(Context context, UserTracker userTracker, UiEventLogger uiEventLogger,
int userId) {
super(context);
setTitle(context.getString(R.string.guest_wipe_session_title));
@@ -175,6 +179,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
setButton(BUTTON_DONTWIPE,
context.getString(R.string.guest_wipe_session_dontwipe), this);
mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
mUserId = userId;
}
@@ -183,7 +188,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(), mUserId);
wipeGuestSession(getContext(), mUserTracker, mUserId);
dismiss();
} else if (which == BUTTON_DONTWIPE) {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE);

View File

@@ -22,6 +22,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -67,9 +68,11 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -98,9 +101,12 @@ public class UserSwitcherController implements Dumpable {
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
protected final Context mContext;
protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
private final ContentObserver mSettingsObserver;
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
private final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
@VisibleForTesting
final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
private final KeyguardStateController mKeyguardStateController;
protected final Handler mHandler;
private final ActivityStarter mActivityStarter;
@@ -109,8 +115,10 @@ public class UserSwitcherController implements Dumpable {
private final IActivityTaskManager mActivityTaskManager;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
private Dialog mExitGuestDialog;
private Dialog mAddUserDialog;
@VisibleForTesting
AlertDialog mExitGuestDialog;
@VisibleForTesting
Dialog mAddUserDialog;
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
@@ -125,17 +133,21 @@ public class UserSwitcherController implements Dumpable {
public final DetailAdapter mUserDetailAdapter;
@Inject
public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
public UserSwitcherController(Context context, UserManager userManager, UserTracker userTracker,
KeyguardStateController keyguardStateController,
@Main Handler handler, ActivityStarter activityStarter,
BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger,
TelephonyListenerManager telephonyListenerManager,
IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter) {
IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter,
SecureSettings secureSettings) {
mContext = context;
mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(mUiEventLogger);
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
mUserTracker, mUiEventLogger, secureSettings);
mUserDetailAdapter = userDetailAdapter;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
@@ -143,7 +155,7 @@ public class UserSwitcherController implements Dumpable {
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mActivityStarter = activityStarter;
mUserManager = UserManager.get(context);
mUserManager = userManager;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -162,6 +174,14 @@ public class UserSwitcherController implements Dumpable {
mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
PERMISSION_SELF, null /* scheduler */);
mSettingsObserver = new ContentObserver(mHandler) {
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
refreshUsers(UserHandle.USER_NULL);
};
};
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
mSettingsObserver);
@@ -223,11 +243,11 @@ public class UserSwitcherController implements Dumpable {
return null;
}
ArrayList<UserRecord> records = new ArrayList<>(infos.size());
int currentId = ActivityManager.getCurrentUser();
int currentId = mUserTracker.getUserId();
// Check user switchability of the foreground user since SystemUI is running in
// User 0
boolean canSwitchUsers = mUserManager.getUserSwitchability(
UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK;
UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
UserInfo currentUserInfo = null;
UserRecord guestRecord = null;
@@ -355,7 +375,7 @@ public class UserSwitcherController implements Dumpable {
}
public void logoutCurrentUser() {
int currentUser = ActivityManager.getCurrentUser();
int currentUser = mUserTracker.getUserId();
if (currentUser != UserHandle.USER_SYSTEM) {
pauseRefreshUsers();
ActivityManager.logoutCurrentUser();
@@ -367,7 +387,7 @@ public class UserSwitcherController implements Dumpable {
Log.w(TAG, "User " + userId + " could not removed.");
return;
}
if (ActivityManager.getCurrentUser() == userId) {
if (mUserTracker.getUserId() == userId) {
switchToUserId(UserHandle.USER_SYSTEM);
}
if (mUserManager.removeUser(userId)) {
@@ -375,7 +395,8 @@ public class UserSwitcherController implements Dumpable {
}
}
private void onUserListItemClicked(UserRecord record) {
@VisibleForTesting
void onUserListItemClicked(UserRecord record) {
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
@@ -401,7 +422,7 @@ public class UserSwitcherController implements Dumpable {
id = record.info.id;
}
int currUserId = ActivityManager.getCurrentUser();
int currUserId = mUserTracker.getUserId();
if (currUserId == id) {
if (record.isGuest) {
showExitGuestDialog(id);
@@ -557,15 +578,6 @@ public class UserSwitcherController implements Dumpable {
}
};
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
refreshUsers(UserHandle.USER_NULL);
};
};
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UserSwitcherController state:");
@@ -701,9 +713,9 @@ public class UserSwitcherController implements Dumpable {
private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId())) {
record.isDisabledByAdmin = true;
record.enforcedAdmin = admin;
} else {
@@ -906,7 +918,8 @@ public class UserSwitcherController implements Dumpable {
}
}
private final class AddUserDialog extends SystemUIDialog implements
@VisibleForTesting
final class AddUserDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
public AddUserDialog(Context context) {

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2021 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.systemui.statusbar.policy
import android.app.IActivityTaskManager
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.os.Handler
import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.UserIcons
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
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.settings.SecureSettings
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class UserSwitcherControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var handler: Handler
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@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 uiEventLogger: UiEventLoggerFake
private lateinit var picture: Bitmap
private val guestId = 1234
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
uiEventLogger = UiEventLoggerFake()
userSwitcherController = UserSwitcherController(context,
userManager,
userTracker,
keyguardStateController,
handler,
activityStarter,
broadcastDispatcher,
uiEventLogger,
telephonyListenerManager,
activityTaskManager,
userDetailAdapter,
secureSettings)
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
}
@Test
fun testAddGuest_okButtonPressed_isLogged() {
val emptyGuestUserRecord = UserSwitcherController.UserRecord(
null,
null,
true /* guest */,
false /* current */,
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
`when`(userManager.createGuest(any(), anyString())).thenReturn(userInfo)
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
}
@Test
fun testRemoveGuest_removeButtonPressed_isLogged() {
val currentGuestUserRecord = UserSwitcherController.UserRecord(
userInfo,
picture,
true /* guest */,
true /* current */,
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
assertNotNull(userSwitcherController.mExitGuestDialog)
userSwitcherController.mExitGuestDialog
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
testableLooper.processAllMessages()
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
}
@Test
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
val currentGuestUserRecord = UserSwitcherController.UserRecord(
userInfo,
picture,
true /* guest */,
true /* current */,
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
assertNotNull(userSwitcherController.mExitGuestDialog)
userSwitcherController.mExitGuestDialog
.getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
testableLooper.processAllMessages()
assertEquals(0, uiEventLogger.numLogs())
}
@Test
fun testWipeGuest_startOverButtonPressed_isLogged() {
val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
val currentGuestUserRecord = UserSwitcherController.UserRecord(
guestInfo,
picture,
true /* guest */,
false /* current */,
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
// Simulate that guest user has already logged in
`when`(secureSettings.getIntForUser(
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
.thenReturn(1)
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
// 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)
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
.getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_WIPE).performClick()
testableLooper.processAllMessages()
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_WIPE.id, uiEventLogger.eventId(0))
}
@Test
fun testWipeGuest_continueButtonPressed_isLogged() {
val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
val currentGuestUserRecord = UserSwitcherController.UserRecord(
guestInfo,
picture,
true /* guest */,
false /* current */,
false /* isAddUser */,
false /* isRestricted */,
true /* isSwitchToEnabled */)
// Simulate that guest user has already logged in
`when`(secureSettings.getIntForUser(
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
.thenReturn(1)
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
// 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)
assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
.getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_DONTWIPE)
.performClick()
testableLooper.processAllMessages()
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE.id, uiEventLogger.eventId(0))
}
}