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

Commit fd230c73 authored by Billy Huang's avatar Billy Huang
Browse files

Add unit test coverage for unlock attempts in TrustManagerServiceTest

The downstream effects of an unlock attempt using a pin/password were not
previously tested within the TrustManagerServiceTest. Add these tests in
preparation for further changes to pin/password unlock reporting to
TrustManagerService in b/323086607.

BUG: b/323086607
Test: `atest TrustManagerServiceTest`
Flag: TEST_ONLY
Change-Id: I8ce35a822c807c5e272a5d3169b1bf55499d47e0
parent 3fc9a27f
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -275,6 +275,10 @@ public class TrustManagerService extends SystemService {
            return KeyStoreAuthorization.getInstance();
        }

        AlarmManager getAlarmManager() {
            return mContext.getSystemService(AlarmManager.class);
        }

        Looper getLooper() {
            return Looper.myLooper();
        }
@@ -293,7 +297,7 @@ public class TrustManagerService extends SystemService {
        mLockPatternUtils = injector.getLockPatternUtils();
        mKeyStoreAuthorization = injector.getKeyStoreAuthorization();
        mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        mAlarmManager = injector.getAlarmManager();
    }

    @Override
+218 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.trust;

import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
@@ -26,12 +28,22 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;

import static java.util.Collections.singleton;

import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
@@ -65,15 +77,22 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.security.KeyStoreAuthorization;
import android.service.trust.GrantTrustResult;
import android.service.trust.ITrustAgentService;
import android.service.trust.ITrustAgentServiceCallback;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;

import androidx.test.core.app.ApplicationProvider;

import com.android.internal.infra.AndroidFuture;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.StrongAuthFlags;
import com.android.internal.widget.LockSettingsInternal;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -81,15 +100,19 @@ import com.android.server.SystemServiceManager;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TrustManagerServiceTest {

@@ -115,21 +138,28 @@ public class TrustManagerServiceTest {
    private static final int PROFILE_USER_ID = 70;
    private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
    private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };
    private static final long RENEWABLE_TRUST_DURATION = 10000L;
    private static final String GRANT_TRUST_MESSAGE = "granted";
    private static final String TAG = "TrustManagerServiceTest";

    private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
    private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
    private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
    private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>();

    private @Mock ActivityManager mActivityManager;
    private @Mock AlarmManager mAlarmManager;
    private @Mock BiometricManager mBiometricManager;
    private @Mock DevicePolicyManager mDevicePolicyManager;
    private @Mock FaceManager mFaceManager;
    private @Mock FingerprintManager mFingerprintManager;
    private @Mock KeyStoreAuthorization mKeyStoreAuthorization;
    private @Mock LockPatternUtils mLockPatternUtils;
    private @Mock LockSettingsInternal mLockSettingsInternal;
    private @Mock PackageManager mPackageManager;
    private @Mock UserManager mUserManager;
    private @Mock IWindowManager mWindowManager;
    private @Mock ITrustListener mTrustListener;

    private HandlerThread mHandlerThread;
    private TrustManagerService mService;
@@ -158,6 +188,9 @@ public class TrustManagerServiceTest {
            return null;
        }).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));

        LocalServices.removeServiceForTest(LockSettingsInternal.class);
        LocalServices.addService(LockSettingsInternal.class, mLockSettingsInternal);

        ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
            @Override
            public boolean matches(Intent argument) {
@@ -176,6 +209,7 @@ public class TrustManagerServiceTest {
        when(mWindowManager.isKeyguardLocked()).thenReturn(true);

        mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
        mMockContext.addMockSystemService(AlarmManager.class, mAlarmManager);
        mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
        mMockContext.addMockSystemService(FaceManager.class, mFaceManager);
        mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
@@ -197,6 +231,7 @@ public class TrustManagerServiceTest {
        verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
                    binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
        mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
        mTrustManager.registerTrustListener(mTrustListener);
    }

    private class MockInjector extends TrustManagerService.Injector {
@@ -214,6 +249,11 @@ public class TrustManagerServiceTest {
            return mKeyStoreAuthorization;
        }

        @Override
        AlarmManager getAlarmManager() {
            return mAlarmManager;
        }

        @Override
        Looper getLooper() {
            return mHandlerThread.getLooper();
@@ -367,12 +407,10 @@ public class TrustManagerServiceTest {

    @Test
    public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
        final ITrustListener trustListener = mock(ITrustListener.class);
        mTrustManager.registerTrustListener(trustListener);
        mService.waitForIdle();
        mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
        mService.waitForIdle();
        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
        verify(mTrustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
    }

    // Tests that when the device is locked for a managed profile with a *unified* challenge, the
@@ -415,6 +453,169 @@ public class TrustManagerServiceTest {
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS), eq(false));
    }

    @Test
    public void testSuccessfulUnlock_bindsTrustAgent() throws Exception {
        when(mUserManager.getAliveUsers())
                .thenReturn(List.of(new UserInfo(TEST_USER_ID, "test", UserInfo.FLAG_FULL)));
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        ITrustAgentService trustAgentService =
                setUpTrustAgentWithStrongAuthRequired(
                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);

        attemptSuccessfulUnlock(TEST_USER_ID);
        mService.waitForIdle();

        assertThat(getCallback(trustAgentService)).isNotNull();
    }

    @Test
    public void testSuccessfulUnlock_notifiesTrustAgent() throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        ITrustAgentService trustAgentService =
                setUpTrustAgentWithStrongAuthRequired(
                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);

        attemptSuccessfulUnlock(TEST_USER_ID);
        mService.waitForIdle();

        verify(trustAgentService).onUnlockAttempt(/* successful= */ true);
    }

    @Test
    public void testSuccessfulUnlock_notifiesTrustListenerOfChangeInManagedTrust()
            throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
        mService.waitForIdle();
        Mockito.reset(mTrustListener);

        attemptSuccessfulUnlock(TEST_USER_ID);
        mService.waitForIdle();

        verify(mTrustListener).onTrustManagedChanged(false, TEST_USER_ID);
    }

    @Test
    @Ignore("TODO: b/340891566 - Trustagent always refreshes trustable timer for user 0 on unlock")
    public void testSuccessfulUnlock_refreshesTrustableTimers() throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        ITrustAgentService trustAgent =
                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
        setUpRenewableTrust(trustAgent);

        attemptSuccessfulUnlock(TEST_USER_ID);
        mService.waitForIdle();

        // Idle and hard timeout alarms for first renewable trust granted
        // Idle timeout alarm refresh for second renewable trust granted
        // Idle and hard timeout alarms refresh for last report
        verify(mAlarmManager, times(3))
                .setExact(
                        eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                        anyLong(),
                        anyString(),
                        any(AlarmManager.OnAlarmListener.class),
                        any(Handler.class));
    }

    @Test
    public void testFailedUnlock_doesNotBindTrustAgent() throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        ITrustAgentService trustAgentService =
                setUpTrustAgentWithStrongAuthRequired(
                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);

        attemptFailedUnlock(TEST_USER_ID);
        mService.waitForIdle();

        verify(trustAgentService, never()).setCallback(any());
    }

    @Test
    public void testFailedUnlock_notifiesTrustAgent() throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        ITrustAgentService trustAgentService =
                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);

        attemptFailedUnlock(TEST_USER_ID);
        mService.waitForIdle();

        verify(trustAgentService).onUnlockAttempt(/* successful= */ false);
    }

    @Test
    public void testFailedUnlock_doesNotNotifyTrustListenerOfChangeInManagedTrust()
            throws Exception {
        ComponentName trustAgentName =
                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
        Mockito.reset(mTrustListener);

        attemptFailedUnlock(TEST_USER_ID);
        mService.waitForIdle();

        verify(mTrustListener, never()).onTrustManagedChanged(anyBoolean(), anyInt());
    }

    private void setUpRenewableTrust(ITrustAgentService trustAgent) throws RemoteException {
        ITrustAgentServiceCallback callback = getCallback(trustAgent);
        callback.setManagingTrust(true);
        mService.waitForIdle();
        attemptSuccessfulUnlock(TEST_USER_ID);
        mService.waitForIdle();
        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
        grantRenewableTrust(callback);
    }

    private ITrustAgentService setUpTrustAgentWithStrongAuthRequired(
            ComponentName agentName, @StrongAuthFlags int strongAuthFlags) throws Exception {
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(TEST_USER_ID);
        addTrustAgent(agentName, true);
        mLockPatternUtils.setKnownTrustAgents(singleton(agentName), TEST_USER_ID);
        mLockPatternUtils.setEnabledTrustAgents(singleton(agentName), TEST_USER_ID);
        when(mUserManager.isUserUnlockingOrUnlocked(TEST_USER_ID)).thenReturn(true);
        setupStrongAuthTracker(strongAuthFlags, false);
        mService.waitForIdle();
        return getOrCreateMockTrustAgent(agentName);
    }

    private void attemptSuccessfulUnlock(int userId) throws RemoteException {
        mTrustManager.reportUnlockAttempt(/* successful= */ true, userId);
    }

    private void attemptFailedUnlock(int userId) throws RemoteException {
        mTrustManager.reportUnlockAttempt(/* successful= */ false, userId);
    }

    private void grantRenewableTrust(ITrustAgentServiceCallback callback) throws RemoteException {
        Log.i(TAG, "Granting trust");
        AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
        callback.grantTrust(
                GRANT_TRUST_MESSAGE,
                RENEWABLE_TRUST_DURATION,
                FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
                future);
        mService.waitForIdle();
    }

    /**
     * Retrieve the ITrustAgentServiceCallback attached to a TrustAgentService after it has been
     * bound to by the TrustManagerService. Will fail if no binding was established.
     */
    private ITrustAgentServiceCallback getCallback(ITrustAgentService trustAgentService)
            throws RemoteException {
        ArgumentCaptor<ITrustAgentServiceCallback> callbackCaptor =
                ArgumentCaptor.forClass(ITrustAgentServiceCallback.class);
        verify(trustAgentService).setCallback(callbackCaptor.capture());
        return callbackCaptor.getValue();
    }

    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
@@ -637,6 +838,20 @@ public class TrustManagerServiceTest {
        ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.serviceInfo = serviceInfo;
        mTrustAgentResolveInfoList.add(resolveInfo);
        ITrustAgentService.Stub mockService = getOrCreateMockTrustAgent(agentComponentName);
        mMockContext.addMockService(agentComponentName, mockService);
        mMockTrustAgents.put(agentComponentName, mockService);
    }

    private ITrustAgentService.Stub getOrCreateMockTrustAgent(ComponentName agentComponentName) {
        return mMockTrustAgents.computeIfAbsent(
                agentComponentName,
                (componentName) -> {
                    ITrustAgentService.Stub mockTrustAgent = mock(ITrustAgentService.Stub.class);
                    when(mockTrustAgent.queryLocalInterface(anyString()))
                            .thenReturn(mockTrustAgent);
                    return mockTrustAgent;
                });
    }

    private void bootService() {