0

[Android][CredMan] Clean up launched WebAuthnAndroid Features

The feature is fully launched and can be cleaned up. While the parameter
for the flag isn't necessary, it should remain in tests to cover the
behaviour on different device types.

Minor fixes:
* guard clauses for what is an easy return now
* ResettersForTesting prevents leaking overrides or cached support
* renamed tests which included the flag name

Fixed: 40284674
Change-Id: I466a7b2f386ab73e2007bbfd60daeac3f1b812b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6263448
Commit-Queue: Friedrich Hauser <friedrichh@chromium.org>
Reviewed-by: Nina Satragno <nsatragno@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1421019}
This commit is contained in:
Friedrich Horschig
2025-02-17 03:45:25 -08:00
committed by Chromium LUCI CQ
parent 8f44711a62
commit be6a3663a7
8 changed files with 95 additions and 239 deletions
chrome/browser/touch_to_fill/password_manager/android/javatests/src/org/chromium/chrome/browser/touch_to_fill
components/webauthn/android
java
src
org
chromium
components
junit
src
org
device/fido
services/device/public
tools/metrics/histograms

@ -944,7 +944,8 @@ public class TouchToFillViewTest {
.getString( .getString(
R.string.touch_to_fill_sheet_passkey_credential_context))); R.string.touch_to_fill_sheet_passkey_credential_context)));
CredManSupportProvider.setupForTesting(/*override*/ false); CredManSupportProvider.setupForTesting(
/* overrideAndroidVersion= */ null, /* overrideForcesGpm= */ null);
} }
private ChromeActivity getActivity() { private ChromeActivity getActivity() {

@ -7,9 +7,12 @@ package org.chromium.components.webauthn.cred_man;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import androidx.annotation.Nullable;
import org.jni_zero.CalledByNative; import org.jni_zero.CalledByNative;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.ResettersForTesting;
import org.chromium.base.ServiceLoaderUtil; import org.chromium.base.ServiceLoaderUtil;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.version_info.VersionInfo; import org.chromium.base.version_info.VersionInfo;
@ -17,8 +20,6 @@ import org.chromium.components.webauthn.CredManSupport;
import org.chromium.components.webauthn.GmsCoreUtils; import org.chromium.components.webauthn.GmsCoreUtils;
import org.chromium.components.webauthn.WebauthnMode; import org.chromium.components.webauthn.WebauthnMode;
import org.chromium.components.webauthn.WebauthnModeProvider; import org.chromium.components.webauthn.WebauthnModeProvider;
import org.chromium.device.DeviceFeatureList;
import org.chromium.device.DeviceFeatureMap;
public class CredManSupportProvider { public class CredManSupportProvider {
private static final int GMSCORE_MIN_VERSION_CANARY_DEV = 241900000; private static final int GMSCORE_MIN_VERSION_CANARY_DEV = 241900000;
@ -26,11 +27,23 @@ public class CredManSupportProvider {
private static @CredManSupport int sCredManSupport; private static @CredManSupport int sCredManSupport;
private static boolean sOverrideVersionCheckForTesting; private static @Nullable Integer sOverrideAndroidVersion;
private static @Nullable Boolean sOverrideForcesGpm;
public static void setupForTesting(boolean override) { public static void setupForTesting(
sOverrideVersionCheckForTesting = override; @Nullable Integer overrideAndroidVersion, @Nullable Boolean overrideForcesGpm) {
sOverrideAndroidVersion = overrideAndroidVersion;
sOverrideForcesGpm = overrideForcesGpm;
sCredManSupport = CredManSupport.NOT_EVALUATED; sCredManSupport = CredManSupport.NOT_EVALUATED;
ResettersForTesting.register(
() -> {
sOverrideAndroidVersion = null;
sOverrideForcesGpm = null;
// While this is not a test-specific value, the state shouldn't leak between
// tests.
sCredManSupport = CredManSupport.NOT_EVALUATED;
});
} }
@CalledByNative @CalledByNative
@ -38,18 +51,15 @@ public class CredManSupportProvider {
if (sCredManSupport != CredManSupport.NOT_EVALUATED) { if (sCredManSupport != CredManSupport.NOT_EVALUATED) {
return sCredManSupport; return sCredManSupport;
} }
if (!sOverrideVersionCheckForTesting) { if (getAndroidVersion() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { sCredManSupport = CredManSupport.DISABLED;
sCredManSupport = CredManSupport.DISABLED; return sCredManSupport;
return sCredManSupport;
}
if (hasOldGmsVersion()) {
sCredManSupport = CredManSupport.DISABLED;
return sCredManSupport;
}
} }
if (notSkippedBecauseInTests() && hasOldGmsVersion()) {
if (!sOverrideVersionCheckForTesting sCredManSupport = CredManSupport.DISABLED;
return sCredManSupport;
}
if (notSkippedBecauseInTests()
&& ContextUtils.getApplicationContext().getSystemService(Context.CREDENTIAL_SERVICE) && ContextUtils.getApplicationContext().getSystemService(Context.CREDENTIAL_SERVICE)
== null) { == null) {
sCredManSupport = CredManSupport.DISABLED; sCredManSupport = CredManSupport.DISABLED;
@ -58,30 +68,20 @@ public class CredManSupportProvider {
} }
recordCredManAvailability(/*available*/ true); recordCredManAvailability(/*available*/ true);
if (DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN)) { final CredManUiRecommender recommender =
CredManUiRecommender recommender = ServiceLoaderUtil.maybeCreate(CredManUiRecommender.class);
ServiceLoaderUtil.maybeCreate(CredManUiRecommender.class); boolean customUiRecommended = recommender != null && recommender.recommendsCustomUi();
boolean customUiRecommended = boolean gpmInCredMan =
recommender == null ? false : recommender.recommendsCustomUi(); sOverrideForcesGpm != null ? sOverrideForcesGpm : customUiRecommended;
boolean gpmInCredMan = boolean isChrome3pPwmMode =
DeviceFeatureMap.getInstance() WebauthnModeProvider.getInstance().getGlobalWebauthnMode()
.getFieldTrialParamByFeatureAsBoolean( == WebauthnMode.CHROME_3PP_ENABLED;
DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN, // In CHROME_3PP_ENABLED mode Chrome does not use FIDO2 APIs parallel with CredMan. This
"gpm_in_cred_man", // is because Chrome's Password Manager capabilities are disabled.
customUiRecommended); sCredManSupport =
boolean isChrome3pPwmMode = gpmInCredMan || isChrome3pPwmMode
WebauthnModeProvider.getInstance().getGlobalWebauthnMode() ? CredManSupport.FULL_UNLESS_INAPPLICABLE
== WebauthnMode.CHROME_3PP_ENABLED; : CredManSupport.PARALLEL_WITH_FIDO_2;
// In CHROME_3PP_ENABLED mode Chrome does not use FIDO2 APIs parallel with CredMan. This
// is because Chrome's Password Manager capabilities are disabled.
sCredManSupport =
gpmInCredMan || isChrome3pPwmMode
? CredManSupport.FULL_UNLESS_INAPPLICABLE
: CredManSupport.PARALLEL_WITH_FIDO_2;
return sCredManSupport;
}
sCredManSupport = CredManSupport.IF_REQUIRED;
return sCredManSupport; return sCredManSupport;
} }
@ -89,16 +89,15 @@ public class CredManSupportProvider {
if (sCredManSupport != CredManSupport.NOT_EVALUATED) { if (sCredManSupport != CredManSupport.NOT_EVALUATED) {
return sCredManSupport; return sCredManSupport;
} }
if (!sOverrideVersionCheckForTesting) { if (getAndroidVersion() < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { sCredManSupport = CredManSupport.DISABLED;
sCredManSupport = CredManSupport.DISABLED; return sCredManSupport;
return sCredManSupport; }
} if (notSkippedBecauseInTests()
if (ContextUtils.getApplicationContext().getSystemService(Context.CREDENTIAL_SERVICE) && ContextUtils.getApplicationContext().getSystemService(Context.CREDENTIAL_SERVICE)
== null) { == null) {
sCredManSupport = CredManSupport.DISABLED; sCredManSupport = CredManSupport.DISABLED;
return sCredManSupport; return sCredManSupport;
}
} }
sCredManSupport = CredManSupport.FULL_UNLESS_INAPPLICABLE; sCredManSupport = CredManSupport.FULL_UNLESS_INAPPLICABLE;
return sCredManSupport; return sCredManSupport;
@ -110,20 +109,18 @@ public class CredManSupportProvider {
} }
private static boolean hasOldGmsVersion() { private static boolean hasOldGmsVersion() {
assert !sOverrideVersionCheckForTesting : "Don't use in testing!"; assert sOverrideAndroidVersion == null : "Don't use in testing!";
int gmsVersion = GmsCoreUtils.getGmsCoreVersion(); // The check works for unavailable and low GMS versions. `getGmsCoreVersion()` is -1 if the
if (gmsVersion == -1) { // GMS version can't be retrieved. Chrome assumes an insufficient GMS availability then.
return true; // Couldn't get a GMS version. Assume insufficient GMS availability. return GmsCoreUtils.getGmsCoreVersion() < getMinGmsVersionForCurrentChannel();
} }
final int requiredMinGmsVersion = private static int getAndroidVersion() {
DeviceFeatureMap.getInstance() return sOverrideAndroidVersion == null ? Build.VERSION.SDK_INT : sOverrideAndroidVersion;
.getFieldTrialParamByFeatureAsInt( }
DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN,
"min_gms_core_version_no_dots",
getMinGmsVersionForCurrentChannel());
return gmsVersion < requiredMinGmsVersion; private static boolean notSkippedBecauseInTests() {
return sOverrideForcesGpm == null && sOverrideAndroidVersion == null;
} }
private static int getMinGmsVersionForCurrentChannel() { private static int getMinGmsVersionForCurrentChannel() {

@ -101,8 +101,6 @@ public class Fido2CredentialRequestRobolectricTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
FeatureOverrides.newBuilder() FeatureOverrides.newBuilder()
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN)
.param("gpm_in_cred_man", true)
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE) .enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE)
.disable(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_BROWSER_BOUND_KEYS) .disable(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_BROWSER_BOUND_KEYS)
.apply(); .apply();
@ -175,8 +173,9 @@ public class Fido2CredentialRequestRobolectricTest {
.performGetAssertionWebAuthSecurityChecks( .performGetAssertionWebAuthSecurityChecks(
any(String.class), any(Origin.class), anyBoolean(), any(Callback.class)); any(String.class), any(Origin.class), anyBoolean(), any(Callback.class));
// Reset any cached evaluation of whether CredMan should be supported. CredManSupportProvider.setupForTesting(
CredManSupportProvider.setupForTesting(true); /* overrideAndroidVersion= */ Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
/* overrideForcesGpm= */ true);
Fido2GetCredentialsComparator.Factory.setInstanceForTesting(mGetCredentialsComparator); Fido2GetCredentialsComparator.Factory.setInstanceForTesting(mGetCredentialsComparator);
mRequest.overrideBrowserBridgeForTesting(mBrowserBridgeMock); mRequest.overrideBrowserBridgeForTesting(mBrowserBridgeMock);
mRequest.setCredManHelperForTesting(mCredManHelperMock); mRequest.setCredManHelperForTesting(mCredManHelperMock);
@ -191,8 +190,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testMakeCredential_credManEnabled() { public void testMakeCredential() {
mRequest.handleMakeCredentialRequest( mRequest.handleMakeCredentialRequest(
mCreationOptions, mCreationOptions,
/* maybeBrowserOptions= */ null, /* maybeBrowserOptions= */ null,
@ -207,25 +205,6 @@ public class Fido2CredentialRequestRobolectricTest {
.startMakeRequest(any(), any(), any(), any(), any(), any()); .startMakeRequest(any(), any(), any(), any(), any(), any());
} }
@Test
@SmallTest
public void testMakeCredential_credManDisabled_notUsed() {
FeatureOverrides.disable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN);
mRequest.handleMakeCredentialRequest(
mCreationOptions,
mBrowserOptions,
mOrigin,
mOrigin,
/* paymentOptions= */ null,
mCallback::onRegisterResponse,
mCallback::onError,
mCallback::onRequestOutcome);
verify(mCredManHelperMock, times(0))
.startMakeRequest(any(), any(), any(), any(), any(), any());
}
@Test @Test
@SmallTest @SmallTest
public void testMakeCredential_rkDiscouraged_goesToPlayServices() { public void testMakeCredential_rkDiscouraged_goesToPlayServices() {
@ -273,7 +252,9 @@ public class Fido2CredentialRequestRobolectricTest {
public void testMakeCredential_webauthnModeAppAndBelowAndroid14_goesToPlayServices() { public void testMakeCredential_webauthnModeAppAndBelowAndroid14_goesToPlayServices() {
Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP); Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE); Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE);
CredManSupportProvider.setupForTesting(/* override= */ false); CredManSupportProvider.setupForTesting(
/* overrideAndroidVersion= */ Build.VERSION_CODES.TIRAMISU,
/* overrideForcesGpm= */ false);
mRequest.handleMakeCredentialRequest( mRequest.handleMakeCredentialRequest(
mCreationOptions, mCreationOptions,
@ -294,7 +275,6 @@ public class Fido2CredentialRequestRobolectricTest {
public void testMakeCredential_webauthnModeAppAndAboveAndroid14_goesToCredMan() { public void testMakeCredential_webauthnModeAppAndAboveAndroid14_goesToCredMan() {
Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP); Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE); Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE);
CredManSupportProvider.setupForTesting(/* override= */ true);
mRequest.handleMakeCredentialRequest( mRequest.handleMakeCredentialRequest(
mCreationOptions, mCreationOptions,
@ -319,8 +299,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_credManEnabledWithGpmInCredManFlag_success() { public void testGetAssertion_success() {
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
mRequestOptions, mRequestOptions,
mOrigin, mOrigin,
@ -346,13 +325,10 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void public void testGetAssertion_GpmNotInCredMan_doesNotCallGetAssertionImmediately() {
testGetAssertion_credManEnabledWithGpmNotInCredManFlag_doesNotCallGetAssertionImmediately() { CredManSupportProvider.setupForTesting(
FeatureOverrides.newBuilder() /* overrideAndroidVersion= */ Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE) /* overrideForcesGpm= */ false);
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN)
.param("gpm_in_cred_man", false)
.apply();
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
mRequestOptions, mRequestOptions,
@ -385,27 +361,6 @@ public class Fido2CredentialRequestRobolectricTest {
assertThat(mFido2ApiCallHelper.mGetAssertionCalled).isFalse(); assertThat(mFido2ApiCallHelper.mGetAssertionCalled).isFalse();
} }
@Test
@SmallTest
public void testGetAssertion_credManDisabled_notUsed() {
FeatureOverrides.newBuilder()
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE)
.disable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN)
.apply();
mRequest.handleGetAssertionRequest(
mRequestOptions,
mOrigin,
mOrigin,
/* payment= */ null,
(responseStatus, response) -> mCallback.onSignResponse(responseStatus, response),
mCallback::onError,
mCallback::onRequestOutcome);
verifyNoInteractions(mCredManHelperMock);
assertThat(mFido2ApiCallHelper.mPasskeyCacheGetCredentialsCalled).isTrue();
}
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_allowListMatchWithExplicitHash_goesToPlayServices() { public void testGetAssertion_allowListMatchWithExplicitHash_goesToPlayServices() {
@ -426,7 +381,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_credManNoCredentialsWithGpmInCredManFlag_fallbackToPlayServices() { public void testGetAssertion_NoCredentials_fallbackToPlayServices() {
mFido2ApiCallHelper.mCredentialsError = new IllegalStateException("injected error"); mFido2ApiCallHelper.mCredentialsError = new IllegalStateException("injected error");
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
@ -526,10 +481,9 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_allowListNoMatchWhenGpmNotInCredMan_goesToCredMan() { public void testGetAssertion_allowListNoMatchWhenGpmNotInCredMan_goesToCredMan() {
FeatureOverrides.newBuilder() CredManSupportProvider.setupForTesting(
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN) /* overrideAndroidVersion= */ Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
.param("gpm_in_cred_man", false) /* overrideForcesGpm= */ false);
.apply();
PublicKeyCredentialDescriptor descriptor = new PublicKeyCredentialDescriptor(); PublicKeyCredentialDescriptor descriptor = new PublicKeyCredentialDescriptor();
descriptor.type = 0; descriptor.type = 0;
@ -555,7 +509,9 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_WebAuthnModeApp_GoesToPlayServices() { public void testGetAssertion_WebAuthnModeApp_GoesToPlayServices() {
CredManSupportProvider.setupForTesting(/* override= */ false); CredManSupportProvider.setupForTesting(
/* overrideAndroidVersion= */ Build.VERSION_CODES.TIRAMISU,
/* overrideForcesGpm= */ false);
Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP); Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE); Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE);
@ -576,7 +532,9 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_WebAuthnModeApp_failsIfGmscoreNotAvailable() { public void testGetAssertion_WebAuthnModeApp_failsIfGmscoreNotAvailable() {
CredManSupportProvider.setupForTesting(/* override= */ false); CredManSupportProvider.setupForTesting(
/* overrideAndroidVersion= */ Build.VERSION_CODES.TIRAMISU,
/* overrideForcesGpm= */ false);
Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP); Mockito.when(mModeProviderMock.getWebauthnMode(any())).thenReturn(WebauthnMode.APP);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE); Mockito.when(mModeProviderMock.getGlobalWebauthnMode()).thenReturn(WebauthnMode.NONE);
@ -601,7 +559,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testConditionalGetAssertion_credManEnabledSuccessWithGpmInCredManFlag_success() { public void testConditionalGetAssertion_success() {
mRequestOptions.mediation = Mediation.CONDITIONAL; mRequestOptions.mediation = Mediation.CONDITIONAL;
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
@ -629,13 +587,11 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testConditionalGetAssertion_credManEnabledSuccessWithGpmNotInCredManFlag_success() { public void testConditionalGetAssertion_GpmNotInCredMan_success() {
mRequestOptions.mediation = Mediation.CONDITIONAL; mRequestOptions.mediation = Mediation.CONDITIONAL;
FeatureOverrides.newBuilder() CredManSupportProvider.setupForTesting(
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE) /* overrideAndroidVersion= */ Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN) /* overrideForcesGpm= */ false);
.param("gpm_in_cred_man", false)
.apply();
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
mRequestOptions, mRequestOptions,
@ -689,8 +645,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void public void testConditionalGetAssertion_RpCancelWhileIdleWithGpmInCredMan_notAllowedError() {
testConditionalGetAssertion_credManEnabledRpCancelWhileIdleWithGpmInCredManFlag_notAllowedError() {
mRequestOptions.mediation = Mediation.CONDITIONAL; mRequestOptions.mediation = Mediation.CONDITIONAL;
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
@ -713,14 +668,11 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void public void testConditionalGetAssertion_RpCancelWhileIdleWithGpmNotInCredMan_notAllowedError() {
testConditionalGetAssertion_credManEnabledRpCancelWhileIdleWithGpmNotInCredManFlag_notAllowedError() {
mRequestOptions.mediation = Mediation.CONDITIONAL; mRequestOptions.mediation = Mediation.CONDITIONAL;
FeatureOverrides.newBuilder() CredManSupportProvider.setupForTesting(
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE) /* overrideAndroidVersion= */ Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN) /* overrideForcesGpm= */ false);
.param("gpm_in_cred_man", false)
.apply();
mRequest.handleGetAssertionRequest( mRequest.handleGetAssertionRequest(
mRequestOptions, mRequestOptions,
@ -784,55 +736,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void public void testConditionalGetAssertion_webauthnModeChrome3pp_goesToCredMan() {
testConditionalGetAssertion_webauthnModeChrome3ppAndCredManDisabled_notImplemented() {
FeatureOverrides.disable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN);
mRequestOptions.mediation = Mediation.CONDITIONAL;
Mockito.when(mModeProviderMock.getWebauthnMode(any()))
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode())
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
mRequest.handleGetAssertionRequest(
mRequestOptions,
mOrigin,
mOrigin,
/* payment= */ null,
mCallback::onSignResponse,
mCallback::onError,
mCallback::onRequestOutcome);
assertThat(mCallback.getStatus()).isEqualTo(AuthenticatorStatus.NOT_IMPLEMENTED);
verifyNoInteractions(mCredManHelperMock);
}
@Test
@SmallTest
public void testGetAssertion_webauthnModeChrome3ppAndCredManDisabled_goesToPlayServices() {
FeatureOverrides.disable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN);
Mockito.when(mModeProviderMock.getWebauthnMode(any()))
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode())
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
mRequest.handleGetAssertionRequest(
mRequestOptions,
mOrigin,
mOrigin,
/* payment= */ null,
mCallback::onSignResponse,
mCallback::onError,
mCallback::onRequestOutcome);
verifyNoInteractions(mCredManHelperMock);
assertThat(mFido2ApiCallHelper.mGetAssertionCalled).isTrue();
}
@Test
@SmallTest
public void testConditionalGetAssertion_webauthnModeChrome3ppAndCredManEnabled_goesToCredMan() {
FeatureOverrides.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN);
mRequestOptions.mediation = Mediation.CONDITIONAL; mRequestOptions.mediation = Mediation.CONDITIONAL;
Mockito.when(mModeProviderMock.getWebauthnMode(any())) Mockito.when(mModeProviderMock.getWebauthnMode(any()))
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED); .thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
@ -856,9 +760,7 @@ public class Fido2CredentialRequestRobolectricTest {
@Test @Test
@SmallTest @SmallTest
public void testGetAssertion_webauthnModeChrome3ppAndCredManEnabled_goesToCredMan() { public void testGetAssertion_webauthnModeChrome3pp_goesToCredMan() {
FeatureOverrides.enable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN);
Mockito.when(mModeProviderMock.getWebauthnMode(any())) Mockito.when(mModeProviderMock.getWebauthnMode(any()))
.thenReturn(WebauthnMode.CHROME_3PP_ENABLED); .thenReturn(WebauthnMode.CHROME_3PP_ENABLED);
Mockito.when(mModeProviderMock.getGlobalWebauthnMode()) Mockito.when(mModeProviderMock.getGlobalWebauthnMode())
@ -878,28 +780,6 @@ public class Fido2CredentialRequestRobolectricTest {
assertThat(mFido2ApiCallHelper.mGetAssertionCalled).isFalse(); assertThat(mFido2ApiCallHelper.mGetAssertionCalled).isFalse();
} }
@Test
@SmallTest
public void
testGetAssertion_webauthnModeChromeAndCredManDisabledAndComparatorDisabled_noPasskeyCacheCall() {
FeatureOverrides.newBuilder()
.disable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE)
.disable(DeviceFeatureList.WEBAUTHN_ANDROID_CRED_MAN)
.apply();
mRequest.handleGetAssertionRequest(
mRequestOptions,
mOrigin,
mOrigin,
/* payment= */ null,
mCallback::onSignResponse,
mCallback::onError,
mCallback::onRequestOutcome);
assertThat(mFido2ApiCallHelper.mFido2GetCredentialsCalled).isTrue();
assertThat(mFido2ApiCallHelper.mPasskeyCacheGetCredentialsCalled).isFalse();
}
private WebauthnCredentialDetails createWebauthnCredential() { private WebauthnCredentialDetails createWebauthnCredential() {
PublicKeyCredentialDescriptor descriptor = new PublicKeyCredentialDescriptor(); PublicKeyCredentialDescriptor descriptor = new PublicKeyCredentialDescriptor();
descriptor.type = 0; descriptor.type = 0;

@ -38,11 +38,6 @@ BASE_FEATURE(kWebAuthCableExtensionAnywhere,
base::FEATURE_DISABLED_BY_DEFAULT); base::FEATURE_DISABLED_BY_DEFAULT);
#if BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_ANDROID)
// Enabled in M129. Remove in or after M132.
BASE_FEATURE(kWebAuthnAndroidCredMan,
"WebAuthenticationAndroidCredMan",
base::FEATURE_ENABLED_BY_DEFAULT);
// Enabled in M132. Remove in or after M135 or when the comparison histograms // Enabled in M132. Remove in or after M135 or when the comparison histograms
// are not needed anymore. // are not needed anymore.
BASE_FEATURE(kWebAuthnAndroidUsePasskeyCache, BASE_FEATURE(kWebAuthnAndroidUsePasskeyCache,

@ -23,15 +23,6 @@ COMPONENT_EXPORT(DEVICE_FIDO)
BASE_DECLARE_FEATURE(kWebAuthCableExtensionAnywhere); BASE_DECLARE_FEATURE(kWebAuthCableExtensionAnywhere);
#if BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_ANDROID)
// Use the Android 14 Credential Manager API.
COMPONENT_EXPORT(DEVICE_FIDO)
BASE_DECLARE_FEATURE(kWebAuthnAndroidCredMan);
// Use the Android 14 Credential Manager API for credentials stored in Gmscore.
COMPONENT_EXPORT(DEVICE_FIDO)
inline constexpr base::FeatureParam<bool> kWebAuthnAndroidGpmInCredMan{
&kWebAuthnAndroidCredMan, "gpm_in_cred_man", false};
// Use the passkey cache service parallel to the FIDO2 module to retrieve // Use the passkey cache service parallel to the FIDO2 module to retrieve
// passkeys from GMSCore. This is for comparison only. // passkeys from GMSCore. This is for comparison only.
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)

@ -22,7 +22,6 @@ namespace {
// services/device/public/cpp/device_features.h or in other locations in the // services/device/public/cpp/device_features.h or in other locations in the
// code base. // code base.
const base::Feature* const kFeaturesExposedToJava[] = { const base::Feature* const kFeaturesExposedToJava[] = {
&device::kWebAuthnAndroidCredMan,
&device::kWebAuthnAndroidUsePasskeyCache, &device::kWebAuthnAndroidUsePasskeyCache,
&device::kWebAuthnEnablePaaskFragment, &device::kWebAuthnEnablePaaskFragment,
&kGenericSensorExtraClasses, &kGenericSensorExtraClasses,

@ -18,7 +18,6 @@ import org.chromium.build.annotations.NullMarked;
@NullMarked @NullMarked
public abstract class DeviceFeatureList { public abstract class DeviceFeatureList {
public static final String GENERIC_SENSOR_EXTRA_CLASSES = "GenericSensorExtraClasses"; public static final String GENERIC_SENSOR_EXTRA_CLASSES = "GenericSensorExtraClasses";
public static final String WEBAUTHN_ANDROID_CRED_MAN = "WebAuthenticationAndroidCredMan";
public static final String WEBAUTHN_ANDROID_USE_PASSKEY_CACHE = public static final String WEBAUTHN_ANDROID_USE_PASSKEY_CACHE =
"WebAuthenticationAndroidUsePasskeyCache"; "WebAuthenticationAndroidUsePasskeyCache";
public static final String BATTERY_STATUS_MANAGER_BROADCAST_RECEIVER_IN_BACKGROUND = public static final String BATTERY_STATUS_MANAGER_BROADCAST_RECEIVER_IN_BACKGROUND =

@ -10308,7 +10308,6 @@ from previous Chrome versions.
label="RTCDisallowPlanBOutsideDeprecationTrial:enabled"/> label="RTCDisallowPlanBOutsideDeprecationTrial:enabled"/>
<int value="-1508852757" label="CreditCardAutofillTouchBar:disabled"/> <int value="-1508852757" label="CreditCardAutofillTouchBar:disabled"/>
<int value="-1508837668" label="VCPortraitRelighting:enabled"/> <int value="-1508837668" label="VCPortraitRelighting:enabled"/>
<int value="-1507810873" label="WebAuthenticationAndroidCredMan:enabled"/>
<int value="-1507722271" label="ImprovedDesksKeyboardShortcuts:enabled"/> <int value="-1507722271" label="ImprovedDesksKeyboardShortcuts:enabled"/>
<int value="-1506880454" <int value="-1506880454"
label="SupervisedUserCommittedInterstitials:disabled"/> label="SupervisedUserCommittedInterstitials:disabled"/>
@ -10850,8 +10849,6 @@ from previous Chrome versions.
<int value="-1300934428" label="ExternalNavigationDebugLogs:enabled"/> <int value="-1300934428" label="ExternalNavigationDebugLogs:enabled"/>
<int value="-1300692087" label="SplitTabStrip:enabled"/> <int value="-1300692087" label="SplitTabStrip:enabled"/>
<int value="-1299599424" label="VcDlcUi:disabled"/> <int value="-1299599424" label="VcDlcUi:disabled"/>
<int value="-1298884037"
label="WebAuthenticationAndroidCredManAndGmsCore:disabled"/>
<int value="-1298273481" label="http2-grease-settings"/> <int value="-1298273481" label="http2-grease-settings"/>
<int value="-1298067767" label="CalendarModelDebugMode:disabled"/> <int value="-1298067767" label="CalendarModelDebugMode:disabled"/>
<int value="-1297664593" label="SafetyHubMagicStack:enabled"/> <int value="-1297664593" label="SafetyHubMagicStack:enabled"/>
@ -11816,8 +11813,6 @@ from previous Chrome versions.
<int value="-917218910" label="FirstPartyVietnameseInput:enabled"/> <int value="-917218910" label="FirstPartyVietnameseInput:enabled"/>
<int value="-916780902" label="ExternalPciDevicesAllowed:enabled"/> <int value="-916780902" label="ExternalPciDevicesAllowed:enabled"/>
<int value="-916419217" label="LensOverlay:disabled"/> <int value="-916419217" label="LensOverlay:disabled"/>
<int value="-916325423"
label="WebAuthenticationAndroidCredManAndGmsCore:enabled"/>
<int value="-915687759" label="CrOSLateBootAudioOffloadCrasDSPToSOF:enabled"/> <int value="-915687759" label="CrOSLateBootAudioOffloadCrasDSPToSOF:enabled"/>
<int value="-915328316" label="CupsIppPrintingBackend:disabled"/> <int value="-915328316" label="CupsIppPrintingBackend:disabled"/>
<int value="-915035507" label="ArcPrintSpoolerExperiment:enabled"/> <int value="-915035507" label="ArcPrintSpoolerExperiment:enabled"/>
@ -16840,7 +16835,6 @@ from previous Chrome versions.
label="SyncOnlySeparateUserDisplayModeForCrOS:enabled"/> label="SyncOnlySeparateUserDisplayModeForCrOS:enabled"/>
<int value="1084972292" label="WebXRAnchors:enabled"/> <int value="1084972292" label="WebXRAnchors:enabled"/>
<int value="1085130793" label="LongPressBackForHistory:disabled"/> <int value="1085130793" label="LongPressBackForHistory:disabled"/>
<int value="1085260595" label="WebAuthenticationAndroidCredMan:disabled"/>
<int value="1085366788" <int value="1085366788"
label="kFileSystemAccessPersistentPermissions:disabled"/> label="kFileSystemAccessPersistentPermissions:disabled"/>
<int value="1085919447" <int value="1085919447"