diff options
| author | 2024-01-08 20:59:17 +0000 | |
|---|---|---|
| committer | 2024-01-08 20:59:17 +0000 | |
| commit | 4b7074b9ad5bb8d44d3b4aeb4b54e135a882e408 (patch) | |
| tree | 45a5fed59bb4fd72607c8093fc32915326d0f9d7 | |
| parent | f839d892844da236e8bf669953c7ad4b0267a1dd (diff) | |
| parent | fa2d2b742168200370af25f3f87af97bdce344cb (diff) | |
Merge "Fix UnlockedDeviceRequired with biometric unlock with unified challenge" into main am: f3a6dcf1d7 am: 6aa86d6610 am: fa2d2b7421
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2878769
Change-Id: I0668bcc22e85550e9469d624fb354c9ee98557a5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
| -rw-r--r-- | services/core/java/com/android/server/trust/TrustManagerService.java | 7 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java | 95 |
2 files changed, 102 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 5c603c2238bd..5d716fc46f7a 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -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); } diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java index c42c735e3c9d..97e94e3c6fc9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java @@ -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, |