Merge "[User Switcher] Add user and guest on bg thread." into tm-qpr-dev am: 57f5c6bcf1
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19324250 Change-Id: I190aa99f5cb86d63ea47878549f97754672b8a8c Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -62,7 +62,6 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.internal.util.LatencyTracker;
|
import com.android.internal.util.LatencyTracker;
|
||||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||||
import com.android.settingslib.users.UserCreatingDialog;
|
import com.android.settingslib.users.UserCreatingDialog;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
|
||||||
import com.android.systemui.Dumpable;
|
import com.android.systemui.Dumpable;
|
||||||
import com.android.systemui.GuestResetOrExitSessionReceiver;
|
import com.android.systemui.GuestResetOrExitSessionReceiver;
|
||||||
import com.android.systemui.GuestResumeSessionReceiver;
|
import com.android.systemui.GuestResumeSessionReceiver;
|
||||||
@@ -939,8 +938,9 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
guestCreationProgressDialog.show();
|
guestCreationProgressDialog.show();
|
||||||
|
|
||||||
// userManager.createGuest will block the thread so post is needed for the dialog to show
|
// userManager.createGuest will block the thread so post is needed for the dialog to show
|
||||||
ThreadUtils.postOnMainThread(() -> {
|
mBgExecutor.execute(() -> {
|
||||||
final int guestId = createGuest();
|
final int guestId = createGuest();
|
||||||
|
mUiExecutor.execute(() -> {
|
||||||
guestCreationProgressDialog.dismiss();
|
guestCreationProgressDialog.dismiss();
|
||||||
if (guestId == UserHandle.USER_NULL) {
|
if (guestId == UserHandle.USER_NULL) {
|
||||||
Toast.makeText(mContext,
|
Toast.makeText(mContext,
|
||||||
@@ -949,6 +949,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
}
|
}
|
||||||
callback.accept(guestId);
|
callback.accept(guestId);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.user;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.UserInfo;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.UserManager;
|
|
||||||
|
|
||||||
import com.android.internal.util.UserIcons;
|
|
||||||
import com.android.settingslib.users.UserCreatingDialog;
|
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class to do the user creation process. It shows a progress dialog, and manages the user
|
|
||||||
* creation
|
|
||||||
*/
|
|
||||||
public class UserCreator {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final UserManager mUserManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public UserCreator(Context context, UserManager userManager) {
|
|
||||||
mContext = context;
|
|
||||||
mUserManager = userManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a progress dialog then starts the user creation process on the main thread.
|
|
||||||
*
|
|
||||||
* @param successCallback is called when the user creation is successful.
|
|
||||||
* @param errorCallback is called when userManager.createUser returns null.
|
|
||||||
* (Exceptions are not handled by this class)
|
|
||||||
*/
|
|
||||||
public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback,
|
|
||||||
Runnable errorCallback) {
|
|
||||||
|
|
||||||
Dialog userCreationProgressDialog = new UserCreatingDialog(mContext);
|
|
||||||
userCreationProgressDialog.show();
|
|
||||||
|
|
||||||
// userManager.createUser will block the thread so post is needed for the dialog to show
|
|
||||||
ThreadUtils.postOnMainThread(() -> {
|
|
||||||
UserInfo user =
|
|
||||||
mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0);
|
|
||||||
if (user == null) {
|
|
||||||
// Couldn't create user for some reason
|
|
||||||
userCreationProgressDialog.dismiss();
|
|
||||||
errorCallback.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Drawable newUserIcon = userIcon;
|
|
||||||
Resources res = mContext.getResources();
|
|
||||||
if (newUserIcon == null) {
|
|
||||||
newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
|
|
||||||
}
|
|
||||||
mUserManager.setUserIcon(
|
|
||||||
user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));
|
|
||||||
|
|
||||||
userCreationProgressDialog.dismiss();
|
|
||||||
successCallback.accept(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.user
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.UserManager
|
||||||
|
import com.android.internal.util.UserIcons
|
||||||
|
import com.android.settingslib.users.UserCreatingDialog
|
||||||
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to do the user creation process. It shows a progress dialog, and manages the user
|
||||||
|
* creation
|
||||||
|
*/
|
||||||
|
class UserCreator @Inject constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val userManager: UserManager,
|
||||||
|
@Main private val mainExecutor: Executor,
|
||||||
|
@Background private val bgExecutor: Executor
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Shows a progress dialog then starts the user creation process on the main thread.
|
||||||
|
*
|
||||||
|
* @param successCallback is called when the user creation is successful.
|
||||||
|
* @param errorCallback is called when userManager.createUser returns null.
|
||||||
|
* (Exceptions are not handled by this class)
|
||||||
|
*/
|
||||||
|
fun createUser(
|
||||||
|
userName: String?,
|
||||||
|
userIcon: Drawable?,
|
||||||
|
successCallback: Consumer<UserInfo?>,
|
||||||
|
errorCallback: Runnable
|
||||||
|
) {
|
||||||
|
val userCreationProgressDialog: Dialog = UserCreatingDialog(context)
|
||||||
|
userCreationProgressDialog.show()
|
||||||
|
|
||||||
|
// userManager.createUser will block the thread so post is needed for the dialog to show
|
||||||
|
bgExecutor.execute {
|
||||||
|
val user = userManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0)
|
||||||
|
mainExecutor.execute main@{
|
||||||
|
if (user == null) {
|
||||||
|
// Couldn't create user for some reason
|
||||||
|
userCreationProgressDialog.dismiss()
|
||||||
|
errorCallback.run()
|
||||||
|
return@main
|
||||||
|
}
|
||||||
|
bgExecutor.execute {
|
||||||
|
var newUserIcon = userIcon
|
||||||
|
val res = context.resources
|
||||||
|
if (newUserIcon == null) {
|
||||||
|
newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false)
|
||||||
|
}
|
||||||
|
userManager.setUserIcon(
|
||||||
|
user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon))
|
||||||
|
}
|
||||||
|
userCreationProgressDialog.dismiss()
|
||||||
|
successCallback.accept(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ import org.mockito.Mockito.doNothing
|
|||||||
import org.mockito.Mockito.doReturn
|
import org.mockito.Mockito.doReturn
|
||||||
import org.mockito.Mockito.eq
|
import org.mockito.Mockito.eq
|
||||||
import org.mockito.Mockito.mock
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
@@ -269,6 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
|
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
uiExecutor.runAllReady()
|
||||||
testableLooper.processAllMessages()
|
testableLooper.processAllMessages()
|
||||||
verify(interactionJankMonitor).begin(any())
|
verify(interactionJankMonitor).begin(any())
|
||||||
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
|
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
|
||||||
@@ -294,6 +297,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
`when`(userManager.createGuest(any())).thenReturn(guestInfo)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
|
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
uiExecutor.runAllReady()
|
||||||
testableLooper.processAllMessages()
|
testableLooper.processAllMessages()
|
||||||
verify(dialogShower).dismiss()
|
verify(dialogShower).dismiss()
|
||||||
}
|
}
|
||||||
@@ -584,4 +589,24 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
broadcastReceiverCaptor.value.onReceive(context, intent)
|
broadcastReceiverCaptor.value.onReceive(context, intent)
|
||||||
verify(cb).onUserSwitched()
|
verify(cb).onUserSwitched()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onUserItemClicked_guest_runsOnBgThread() {
|
||||||
|
val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java)
|
||||||
|
val guestUserRecord = UserSwitcherController.UserRecord(
|
||||||
|
null,
|
||||||
|
picture,
|
||||||
|
true /* guest */,
|
||||||
|
false /* current */,
|
||||||
|
false /* isAddUser */,
|
||||||
|
false /* isRestricted */,
|
||||||
|
true /* isSwitchToEnabled */,
|
||||||
|
false /* isAddSupervisedUser */)
|
||||||
|
|
||||||
|
userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower)
|
||||||
|
assertTrue(bgExecutor.numPending() > 0)
|
||||||
|
verify(userManager, never()).createGuest(context)
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
verify(userManager).createGuest(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.android.systemui.user
|
||||||
|
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.os.UserManager
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
class UserCreatorTest : SysuiTestCase() {
|
||||||
|
companion object {
|
||||||
|
const val USER_NAME = "abc"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var userCreator: UserCreator
|
||||||
|
@Mock
|
||||||
|
private lateinit var userManager: UserManager
|
||||||
|
private lateinit var mainExecutor: FakeExecutor
|
||||||
|
private lateinit var bgExecutor: FakeExecutor
|
||||||
|
private lateinit var user: UserInfo
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
mainExecutor = FakeExecutor(FakeSystemClock())
|
||||||
|
bgExecutor = FakeExecutor(FakeSystemClock())
|
||||||
|
userCreator = UserCreator(context, userManager, mainExecutor, bgExecutor)
|
||||||
|
user = Mockito.mock(UserInfo::class.java)
|
||||||
|
Mockito.`when`(userManager.createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0))
|
||||||
|
.thenReturn(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCreateUser_threadingOrder() {
|
||||||
|
val successCallback = Mockito.mock(Consumer::class.java)
|
||||||
|
val errorCallback = Mockito.mock(Runnable::class.java)
|
||||||
|
|
||||||
|
userCreator.createUser(
|
||||||
|
USER_NAME,
|
||||||
|
null,
|
||||||
|
successCallback as Consumer<UserInfo?>,
|
||||||
|
errorCallback)
|
||||||
|
|
||||||
|
verify(userManager, never()).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
verify(successCallback, never()).accept(user)
|
||||||
|
mainExecutor.runAllReady()
|
||||||
|
verify(userManager, never()).setUserIcon(anyInt(), any(Bitmap::class.java))
|
||||||
|
bgExecutor.runAllReady()
|
||||||
|
|
||||||
|
verify(userManager).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)
|
||||||
|
verify(userManager).setUserIcon(anyInt(), any(Bitmap::class.java))
|
||||||
|
verify(successCallback).accept(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user