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

Commit 4b7074b9 authored by Eric Biggers's avatar Eric Biggers Committed by Automerger Merge Worker
Browse files

Merge "Fix UnlockedDeviceRequired with biometric unlock with unified...

Merge "Fix UnlockedDeviceRequired with biometric unlock with unified challenge" into main am: f3a6dcf1 am: 6aa86d66 am: fa2d2b74

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2878769



Change-Id: I0668bcc22e85550e9469d624fb354c9ee98557a5
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents f839d892 fa2d2b74
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1440,6 +1440,13 @@ public class TrustManagerService extends SystemService {
        if (biometricManager == null) {
            return new long[0];
        }
        if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()
                && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) {
            // Profiles with unified challenge have their own set of biometrics, but the device
            // unlock happens via the parent user.  In this case Keystore needs to be given the list
            // of biometric SIDs from the parent user, not the profile.
            userId = resolveProfileParent(userId);
        }
        return biometricManager.getAuthenticatorIds(userId);
    }

+95 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
@@ -45,7 +46,9 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -53,7 +56,12 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.security.Authorization;
import android.security.authorization.IKeystoreAuthorization;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
import android.view.IWindowManager;
@@ -83,23 +91,34 @@ public class TrustManagerServiceTest {

    @Rule
    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
            .spyStatic(ActivityManager.class)
            .spyStatic(Authorization.class)
            .mockStatic(ServiceManager.class)
            .mockStatic(WindowManagerGlobal.class)
            .build();

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Rule
    public final MockContext mMockContext = new MockContext(
            ApplicationProvider.getApplicationContext());

    private static final String URI_SCHEME_PACKAGE = "package";
    private static final int TEST_USER_ID = 50;
    private static final int PARENT_USER_ID = 60;
    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 final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
    private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
    private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();

    private @Mock ActivityManager mActivityManager;
    private @Mock BiometricManager mBiometricManager;
    private @Mock DevicePolicyManager mDevicePolicyManager;
    private @Mock IKeystoreAuthorization mKeystoreAuthorization;
    private @Mock LockPatternUtils mLockPatternUtils;
    private @Mock PackageManager mPackageManager;
    private @Mock UserManager mUserManager;
@@ -113,6 +132,9 @@ public class TrustManagerServiceTest {
    @Before
    public void setUp() throws Exception {
        when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true);
        doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService());

        doReturn(mKeystoreAuthorization).when(() -> Authorization.getService());

        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
        when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
@@ -146,6 +168,7 @@ public class TrustManagerServiceTest {
        when(mWindowManager.isKeyguardLocked()).thenReturn(true);

        mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
        mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
        mMockContext.setMockPackageManager(mPackageManager);
        mMockContext.addMockSystemService(UserManager.class, mUserManager);
        doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
@@ -322,6 +345,73 @@ public class TrustManagerServiceTest {
        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
    }

    // Tests that when the device is locked for a managed profile with a *unified* challenge, the
    // device locked notification that is sent to Keystore contains the biometric SIDs of the parent
    // user, not the profile.  This matches the authentication that is needed to unlock the device
    // for the profile again.
    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids()
            throws Exception {
        setupMocksForProfile(/* unifiedChallenge= */ true);

        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization).onDeviceUnlocked(PARENT_USER_ID, null);
        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);

        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
    }

    // Tests that when the device is locked for a managed profile with a *separate* challenge, the
    // device locked notification that is sent to Keystore contains the biometric SIDs of the
    // profile itself.  This matches the authentication that is needed to unlock the device for the
    // profile again.
    @Test
    public void testLockDeviceForManagedProfileWithSeparateChallenge_usesProfileBiometricSids()
            throws Exception {
        setupMocksForProfile(/* unifiedChallenge= */ false);

        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, false);
        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);

        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true);
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS));
    }

    private void setupMocksForProfile(boolean unifiedChallenge) {
        UserInfo parent = new UserInfo(PARENT_USER_ID, "parent", UserInfo.FLAG_FULL);
        UserInfo profile = new UserInfo(PROFILE_USER_ID, "profile", UserInfo.FLAG_MANAGED_PROFILE);
        when(mUserManager.getAliveUsers()).thenReturn(List.of(parent, profile));
        when(mUserManager.getUserInfo(PARENT_USER_ID)).thenReturn(parent);
        when(mUserManager.getUserInfo(PROFILE_USER_ID)).thenReturn(profile);
        when(mUserManager.getProfileParent(PROFILE_USER_ID)).thenReturn(parent);
        when(mUserManager.getEnabledProfileIds(PARENT_USER_ID))
                .thenReturn(new int[] { PROFILE_USER_ID });

        when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
        when(mLockPatternUtils.isProfileWithUnifiedChallenge(PROFILE_USER_ID))
                .thenReturn(unifiedChallenge);
        when(mLockPatternUtils.isManagedProfileWithUnifiedChallenge(PROFILE_USER_ID))
                .thenReturn(unifiedChallenge);
        when(mLockPatternUtils.isSeparateProfileChallengeEnabled(PROFILE_USER_ID))
                .thenReturn(!unifiedChallenge);

        when(mBiometricManager.getAuthenticatorIds(PARENT_USER_ID))
                .thenReturn(PARENT_BIOMETRIC_SIDS);
        when(mBiometricManager.getAuthenticatorIds(PROFILE_USER_ID))
                .thenReturn(PROFILE_BIOMETRIC_SIDS);

        bootService();
        mService.onUserSwitching(null, new SystemService.TargetUser(parent));
    }

    private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        if (isSystemApp) {
@@ -378,6 +468,11 @@ public class TrustManagerServiceTest {
                    scheduler);
        }

        @Override
        public void sendBroadcastAsUser(Intent intent, UserHandle user,
                @Nullable String receiverPermission, @Nullable Bundle options) {
        }

        void sendPackageChangedBroadcast(ComponentName changedComponent) {
            Intent intent = new Intent(
                    Intent.ACTION_PACKAGE_CHANGED,