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

Commit 1ce603c3 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Add a test mode to SecureSettingsWrapper

This is a preparation before moving

  InputMethodManagerService#mHardwareKeyboardShortcutController

to UserData so that each user can keep maintaining its own instance.

The issue is that currently UserDataRepositoryTest does not mock
anything around secure settings and UserManagerInternal, which means
that when it triggers logic that internally accesses to
SecureSettingsWrapper, it ends up throwing NullPointerException as

  LocalServices.getService(UserManagerInternal.class)

returns null, and even if we mocked UserManagerInternal, we still face
a problem on how SecureSettingsWrapper should be have when the unit
test passes a nonexistent user (e.g. 1).

As an intermediate solution, this CL introduces an official fake
implementation of SecureSettingsWrapper, which basically forwards all
the read/write operations to an on memory dictionary rather than
exposing the real data. This should be sufficient for now.

Bug: 305849394
Test: presubmit
Flag: TEST_ONLY
Change-Id: I7cdef44c32871d65d9d2ce2bcdf321cc912d9992
parent 3daf8e93
Loading
Loading
Loading
Loading
+86 −0
Original line number Diff line number Diff line
@@ -44,6 +44,32 @@ final class SecureSettingsWrapper {
    @Nullable
    private static volatile ContentResolver sContentResolver = null;

    private static volatile boolean sTestMode = false;

    /**
     * Can be called from unit tests to start the test mode, where a fake implementation will be
     * used instead.
     *
     * <p>The fake implementation is just an {@link ArrayMap}. By default it is empty, and the data
     * written can be read back later.</p>
     */
    @AnyThread
    static void startTestMode() {
        sTestMode = true;
    }

    /**
     * Can be called from unit tests to end the test mode, where a fake implementation will be used
     * instead.
     */
    @AnyThread
    static void endTestMode() {
        synchronized (sUserMap) {
            sUserMap.clear();
        }
        sTestMode = false;
    }

    /**
     * Not intended to be instantiated.
     */
@@ -78,6 +104,52 @@ final class SecureSettingsWrapper {
        int getInt(String key, int defaultValue);
    }

    private static class FakeReaderWriterImpl implements ReaderWriter {
        @GuardedBy("mNonPersistentKeyValues")
        private final ArrayMap<String, String> mNonPersistentKeyValues = new ArrayMap<>();

        @AnyThread
        @Override
        public void putString(String key, String value) {
            synchronized (mNonPersistentKeyValues) {
                mNonPersistentKeyValues.put(key, value);
            }
        }

        @AnyThread
        @Nullable
        @Override
        public String getString(String key, String defaultValue) {
            synchronized (mNonPersistentKeyValues) {
                if (mNonPersistentKeyValues.containsKey(key)) {
                    final String result = mNonPersistentKeyValues.get(key);
                    return result != null ? result : defaultValue;
                }
                return defaultValue;
            }
        }

        @AnyThread
        @Override
        public void putInt(String key, int value) {
            synchronized (mNonPersistentKeyValues) {
                mNonPersistentKeyValues.put(key, String.valueOf(value));
            }
        }

        @AnyThread
        @Override
        public int getInt(String key, int defaultValue) {
            synchronized (mNonPersistentKeyValues) {
                if (mNonPersistentKeyValues.containsKey(key)) {
                    final String result = mNonPersistentKeyValues.get(key);
                    return result != null ? Integer.parseInt(result) : defaultValue;
                }
                return defaultValue;
            }
        }
    }

    private static class UnlockedUserImpl implements ReaderWriter {
        @UserIdInt
        private final int mUserId;
@@ -200,6 +272,9 @@ final class SecureSettingsWrapper {

    private static ReaderWriter createImpl(@NonNull UserManagerInternal userManagerInternal,
            @UserIdInt int userId) {
        if (sTestMode) {
            return new FakeReaderWriterImpl();
        }
        return userManagerInternal.isUserUnlockingOrUnlocked(userId)
                ? new UnlockedUserImpl(userId, sContentResolver)
                : new LockedUserImpl(userId, sContentResolver);
@@ -234,6 +309,9 @@ final class SecureSettingsWrapper {
                return readerWriter;
            }
        }
        if (sTestMode) {
            return putOrGet(userId, new FakeReaderWriterImpl());
        }
        final UserManagerInternal userManagerInternal =
                LocalServices.getService(UserManagerInternal.class);
        if (!userManagerInternal.exists(userId)) {
@@ -276,6 +354,10 @@ final class SecureSettingsWrapper {
     */
    @AnyThread
    static void onUserStarting(@UserIdInt int userId) {
        if (sTestMode) {
            putOrGet(userId, new FakeReaderWriterImpl());
            return;
        }
        putOrGet(userId, createImpl(LocalServices.getService(UserManagerInternal.class), userId));
    }

@@ -286,6 +368,10 @@ final class SecureSettingsWrapper {
     */
    @AnyThread
    static void onUserUnlocking(@UserIdInt int userId) {
        if (sTestMode) {
            putOrGet(userId, new FakeReaderWriterImpl());
            return;
        }
        final ReaderWriter readerWriter = new UnlockedUserImpl(userId, sContentResolver);
        putOrGet(userId, readerWriter);
    }
+7 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.platform.test.ravenwood.RavenwoodRule;

import com.android.server.pm.UserManagerInternal;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -63,6 +64,7 @@ public final class UserDataRepositoryTest {
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        SecureSettingsWrapper.startTestMode();
        mHandler = new Handler(Looper.getMainLooper());
        mBindingControllerFactory = new IntFunction<InputMethodBindingController>() {

@@ -73,6 +75,11 @@ public final class UserDataRepositoryTest {
        };
    }

    @After
    public void tearDown() {
        SecureSettingsWrapper.endTestMode();
    }

    @Test
    public void testUserDataRepository_addsNewUserInfoOnUserCreatedEvent() {
        // Create UserDataRepository and capture the user lifecycle listener