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

Commit ac8d93bf authored by Aaron Liu's avatar Aaron Liu
Browse files

[User Switcher] Add user and guest on bg thread.

Adds user and switches to user on bg thread. Pixel Launcher keeps
stopping dialog shows, but this is from a Log.wtf that is in launcher
code.

Bug: 234546491
Test: Manual on device
Change-Id: Idfc45e7983fabbc4668de7cb9fd8149fdce20e6c
parent 0b0822b3
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;
@@ -936,8 +935,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,
@@ -946,6 +946,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)
    }
}