Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57f5c6bc authored by Aaron Liu's avatar Aaron Liu Committed by Android (Google) Code Review
Browse files

Merge "[User Switcher] Add user and guest on bg thread." into tm-qpr-dev

parents a5cf66dc ac8d93bf
Loading
Loading
Loading
Loading
+10 −9
Original line number Diff line number Diff line
@@ -62,7 +62,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.users.UserCreatingDialog;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.GuestResetOrExitSessionReceiver;
import com.android.systemui.GuestResumeSessionReceiver;
@@ -939,8 +938,9 @@ public class UserSwitcherController implements Dumpable {
        guestCreationProgressDialog.show();

        // userManager.createGuest will block the thread so post is needed for the dialog to show
        ThreadUtils.postOnMainThread(() -> {
        mBgExecutor.execute(() -> {
            final int guestId = createGuest();
            mUiExecutor.execute(() -> {
                guestCreationProgressDialog.dismiss();
                if (guestId == UserHandle.USER_NULL) {
                    Toast.makeText(mContext,
@@ -949,6 +949,7 @@ public class UserSwitcherController implements Dumpable {
                }
                callback.accept(guestId);
            });
        });
    }

    /**
+81 −0
Original line number Diff line number Diff line
@@ -13,39 +13,31 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.user

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;
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
 */
public class UserCreator {

    private final Context mContext;
    private final UserManager mUserManager;

    @Inject
    public UserCreator(Context context, UserManager userManager) {
        mContext = context;
        mUserManager = userManager;
    }

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.
     *
@@ -53,33 +45,37 @@ public class UserCreator {
     * @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();
    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
        ThreadUtils.postOnMainThread(() -> {
            UserInfo user =
                    mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0);
        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;
                    userCreationProgressDialog.dismiss()
                    errorCallback.run()
                    return@main
                }

            Drawable newUserIcon = userIcon;
            Resources res = mContext.getResources();
                bgExecutor.execute {
                    var newUserIcon = userIcon
                    val res = context.resources
                    if (newUserIcon == null) {
                newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
                        newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false)
                    }
                    userManager.setUserIcon(
                        user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon))
                }
                userCreationProgressDialog.dismiss()
                successCallback.accept(user)
            }
        }
            mUserManager.setUserIcon(
                    user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));

            userCreationProgressDialog.dismiss();
            successCallback.accept(user);
        });
    }
}
+25 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -269,6 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
        `when`(userManager.createGuest(any())).thenReturn(guestInfo)

        userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
        bgExecutor.runAllReady()
        uiExecutor.runAllReady()
        testableLooper.processAllMessages()
        verify(interactionJankMonitor).begin(any())
        verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
@@ -294,6 +297,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
        `when`(userManager.createGuest(any())).thenReturn(guestInfo)

        userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
        bgExecutor.runAllReady()
        uiExecutor.runAllReady()
        testableLooper.processAllMessages()
        verify(dialogShower).dismiss()
    }
@@ -584,4 +589,24 @@ class UserSwitcherControllerTest : SysuiTestCase() {
        broadcastReceiverCaptor.value.onReceive(context, intent)
        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)
    }
}
+73 −0
Original line number Diff line number Diff line
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)
    }
}