0

[Scrip] Create GoogleServiceAuthError inside AuthException

This cl creates GoogleServiceAuthError inside AuthException instead of
just keeping a boolean isTransient signal. It also encapsulates the
logic behind creating this error instead of letting clients specify
its type (which was the case before).
All clients that depended on AuthException.isTransientError has been
refactored to take GoogleServiceAuthError instead.

Bug: 404745044
Change-Id: I6fc4de5f83048d306d67e1ae61dbb58ae8920d9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6402393
Reviewed-by: Boris Sazonov <bsazonov@chromium.org>
Commit-Queue: Tanmoy Mollik <triploblastic@google.com>
Cr-Commit-Position: refs/heads/main@{#1444080}
This commit is contained in:
Tanmoy Mollik
2025-04-08 06:17:49 -07:00
committed by Chromium LUCI CQ
parent b3e92d0218
commit 6749bc6fac
13 changed files with 199 additions and 139 deletions

@ -447,7 +447,7 @@ void JNI_ProfileOAuth2TokenServiceDelegate_OnOAuth2TokenFetched(
JNIEnv* env,
const JavaParamRef<jstring>& authToken,
const jlong expiration_time_secs,
jboolean isTransientError,
GoogleServiceAuthError& authError,
jlong nativeCallback) {
std::string token;
if (authToken) {
@ -455,18 +455,9 @@ void JNI_ProfileOAuth2TokenServiceDelegate_OnOAuth2TokenFetched(
}
std::unique_ptr<FetchOAuth2TokenCallback> heap_callback(
reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
GoogleServiceAuthError err = GoogleServiceAuthError::AuthErrorNone();
if (!authToken) {
err =
isTransientError
? GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)
: GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_SERVER);
}
std::move(*heap_callback)
.Run(err, token,
.Run(authError, token,
base::Time::FromSecondsSinceUnixEpoch(expiration_time_secs));
}
} // namespace signin

@ -10,6 +10,7 @@ android_library("java") {
"//build/android:build_java",
"//components/cached_flags:java",
"//components/externalauth/android:java",
"//google_apis/gaia/android:java",
"//net/android:net_java",
"//third_party/android_deps:chromium_play_services_availability_java",
"//third_party/androidx:androidx_annotation_annotation_java",
@ -125,6 +126,7 @@ android_library("signin_java_test_support") {
"//base:base_java",
"//base:base_java_test_support",
"//content/public/test/android:content_java_test_support",
"//google_apis/gaia/android:java",
"//third_party/android_deps:guava_android_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
@ -168,6 +170,7 @@ android_library("unit_device_javatests") {
"//base:base_java",
"//base:base_java_test_support",
"//content/public/test/android:content_java_test_support",
"//google_apis/gaia/android:java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_test_monitor_java",
"//third_party/androidx:androidx_test_runner_java",

@ -20,6 +20,7 @@ import org.chromium.build.annotations.Nullable;
import org.chromium.components.signin.base.AccountCapabilities;
import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import java.util.List;
@ -38,10 +39,9 @@ public interface AccountManagerFacade {
/**
* Invoked on the UI thread if no token is available.
*
* @param isTransientError Indicates if the error is transient (network timeout or
* unavailable, etc) or persistent (bad credentials, permission denied, etc).
* @param authError The {@link GoogleServiceAuthError} encountered during token fetch.
*/
void onGetTokenFailure(boolean isTransientError);
void onGetTokenFailure(GoogleServiceAuthError authError);
}
// TODO(crbug.com/40201126): consider refactoring this interface to use Promises.

@ -36,6 +36,8 @@ import org.chromium.components.signin.base.AccountCapabilities;
import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.base.GaiaId;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import java.util.ArrayList;
import java.util.Arrays;
@ -157,13 +159,14 @@ public class AccountManagerFacadeImpl implements AccountManagerFacade {
assert scope != null;
if (mDisallowTokenRequestsForTesting) {
callback.onGetTokenFailure(false);
callback.onGetTokenFailure(
new GoogleServiceAuthError(GoogleServiceAuthErrorState.REQUEST_CANCELED));
return;
}
pendingRequestStarted();
ConnectionRetry.runAuthTask(
new AuthTask<AccessTokenData>() {
new AuthTask() {
@Override
public AccessTokenData run() throws AuthException {
return mDelegate.getAccessToken(
@ -172,14 +175,15 @@ public class AccountManagerFacadeImpl implements AccountManagerFacade {
}
@Override
public void onSuccess(AccessTokenData token) {
public void onSuccess(@Nullable AccessTokenData token) {
assert token != null : "AccessTokenData must not be null on success.";
callback.onGetTokenSuccess(token);
pendingRequestFinished();
}
@Override
public void onFailure(boolean isTransientError) {
callback.onGetTokenFailure(isTransientError);
public void onFailure(GoogleServiceAuthError authError) {
callback.onGetTokenFailure(authError);
pendingRequestFinished();
}
});
@ -212,22 +216,22 @@ public class AccountManagerFacadeImpl implements AccountManagerFacade {
return;
}
ConnectionRetry.runAuthTask(
new AuthTask<Void>() {
new AuthTask() {
@Override
public Void run() throws AuthException {
public @Nullable AccessTokenData run() throws AuthException {
mDelegate.invalidateAccessToken(accessToken);
return null;
}
@Override
public void onSuccess(Void ignored) {
public void onSuccess(@Nullable AccessTokenData ignored) {
if (completedRunnable != null) {
completedRunnable.run();
}
}
@Override
public void onFailure(boolean ignored) {
public void onFailure(GoogleServiceAuthError ignored) {
if (completedRunnable != null) {
completedRunnable.run();
}

@ -5,55 +5,62 @@
package org.chromium.components.signin;
import org.chromium.build.annotations.NullMarked;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
/**
* AuthException abstracts away authenticator specific exceptions behind a single interface.
* It is used for passing information that is useful for better handling of errors.
* AuthException abstracts away authenticator specific exceptions behind a single interface. It is
* used for passing information that is useful for better handling of errors.
*/
@NullMarked
public class AuthException extends Exception {
// TODO(crbug.com/404745044): Remove these fields.
public static final boolean TRANSIENT = true;
public static final boolean NONTRANSIENT = false;
private final boolean mIsTransientError;
private final GoogleServiceAuthError mAuthError;
/**
* Wraps exception that caused auth failure along with transience flag.
* @param isTransientError Whether the error is transient and we can retry.
* Use {@link #TRANSIENT} and {@link #NONTRANSIENT} for readability.
*
* @param cause Exception that caused auth failure.
*/
public AuthException(boolean isTransientError, Exception cause) {
super(cause);
mIsTransientError = isTransientError;
// TODO(crbug.com/404745044): Remove this method.
public AuthException(boolean isTransient, Exception cause) {
this(isTransient, "", cause);
}
/**
* Wraps exception that caused auth failure along with transience flag and message.
* @param isTransientError Whether the error is transient and we can retry.
* Use {@link #TRANSIENT} and {@link #NONTRANSIENT} for readability.
*
* @param message Message describing context in which auth failure happened.
* @param cause Exception that caused auth failure.
* @param ex Exception that caused auth failure.
*/
public AuthException(boolean isTransientError, String message, Exception cause) {
super(message, cause);
mIsTransientError = isTransientError;
// TODO(crbug.com/404745044): Remove this method.
public AuthException(boolean isTransient, String message, Exception ex) {
super(message, ex);
mAuthError =
new GoogleServiceAuthError(
isTransient
? GoogleServiceAuthErrorState.CONNECTION_FAILED
: GoogleServiceAuthErrorState.INVALID_GAIA_CREDENTIALS);
}
/**
* Constructs an instance without a wrapped exception, based on transience flag and message.
* @param isTransientError Whether the error is transient and we can retry.
* Use {@link #TRANSIENT} and {@link #NONTRANSIENT} for readability.
* Wraps exception that caused auth failure and stores the {@link GoogleServiceAuthError}.
*
* @param message Message describing context in which auth failure happened.
* @param ex Exception that caused auth failure.
* @param error {@link GoogleServiceAuthError} encountered during authentication.
*/
public AuthException(boolean isTransientError, String message) {
super(message);
mIsTransientError = isTransientError;
public AuthException(String message, Exception ex, GoogleServiceAuthError error) {
super(message, ex);
mAuthError = error;
}
/** @return Whether the error is transient and we can retry. */
public boolean isTransientError() {
return mIsTransientError;
/** Returns the {@link GoogleServiceAuthError} encountered during token fetch. */
public GoogleServiceAuthError getAuthError() {
return mAuthError;
}
/** Joins messages from all exceptions in the causal chain into a single string. */

@ -4,89 +4,95 @@
package org.chromium.components.signin;
import androidx.annotation.MainThread;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.AsyncTask;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import org.chromium.net.NetworkChangeNotifier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A helper class to encapsulate network connection retry logic for AuthTasks.
*
* The task will be run on the background thread. If it encounters a transient error, it
* will wait for a network change and retry up to {@link #MAX_TRIES} times.
*
* @param <T> Return type of the AuthTask launched by ConnectionRetry.
* <p>The task will be run on the background thread. If it encounters a transient error, it will
* wait for a network change and retry up to {@link #MAX_TRIES} times.
*/
@NullMarked
public class ConnectionRetry<T> implements NetworkChangeNotifier.ConnectionTypeObserver {
public class ConnectionRetry implements NetworkChangeNotifier.ConnectionTypeObserver {
/**
* Authentication Task used together with ConnectionRetry class.
* @param <T> Return type of the AuthTask.
* Authentication Task used together with ConnectionRetry class. If a transient error occurs the
* task may be tried again {@link #MAX_TRIES} number of times.
*/
public interface AuthTask<T> {
public interface AuthTask {
/** Runs the AuthTask. */
T run() throws AuthException;
@Nullable AccessTokenData run() throws AuthException;
/** Called with the result when the AuthTask succeeded. */
default void onSuccess(T result) {}
void onSuccess(@Nullable AccessTokenData result);
/** Called with the result when the AuthTask failed. */
default void onFailure(boolean isTransientError) {}
/** Called with the {@link GoogleServiceAuthError} when the AuthTask failed. */
void onFailure(GoogleServiceAuthError authError);
}
private static final String TAG = "AuthTaskRetry";
private static final int MAX_TRIES = 3;
private final AuthTask<T> mAuthTask;
private final AuthTask mAuthTask;
private final AtomicInteger mNumTries;
private final AtomicBoolean mIsTransientError;
/** Run the given {@link AuthTask} with {@link #MAX_TRIES} times. */
public static <T> void runAuthTask(AuthTask<T> authTask) {
new ConnectionRetry<>(authTask).attempt();
@MainThread
public static void runAuthTask(AuthTask authTask) {
ThreadUtils.assertOnUiThread();
new ConnectionRetry(authTask).attempt();
}
private ConnectionRetry(AuthTask<T> authTask) {
private ConnectionRetry(AuthTask authTask) {
mAuthTask = authTask;
mNumTries = new AtomicInteger(0);
mIsTransientError = new AtomicBoolean(false);
}
/**
* Tries running the {@link AuthTask} in the background. This object is never registered
* as a {@link NetworkChangeNotifier.ConnectionTypeObserver} when this method is called.
* Tries running the {@link AuthTask} in the background. This object is never registered as a
* {@link NetworkChangeNotifier.ConnectionTypeObserver} when this method is called.
*/
private void attempt() {
ThreadUtils.assertOnUiThread();
// Clear any transient error.
mIsTransientError.set(false);
new AsyncTask<@Nullable T>() {
new AsyncTask<@Nullable AccessTokenData>() {
private GoogleServiceAuthError mAuthError =
new GoogleServiceAuthError(GoogleServiceAuthErrorState.NONE);
@Override
public @Nullable T doInBackground() {
public @Nullable AccessTokenData doInBackground() {
try {
return mAuthTask.run();
} catch (AuthException ex) {
Log.w(TAG, "Failed to perform auth task: %s", ex.stringifyCausalChain());
mIsTransientError.set(ex.isTransientError());
mAuthError = ex.getAuthError();
return null;
}
return null;
}
@Override
public void onPostExecute(@Nullable T result) {
if (result != null) {
public void onPostExecute(@Nullable AccessTokenData result) {
if (mAuthError.getState() == GoogleServiceAuthErrorState.NONE) {
mAuthTask.onSuccess(result);
} else if (!mIsTransientError.get()
return;
}
if (!mAuthError.isTransientError()
|| mNumTries.incrementAndGet() >= MAX_TRIES
|| !NetworkChangeNotifier.isInitialized()) {
// Permanent error, ran out of tries, or we can't listen for network
// change events; give up.
mAuthTask.onFailure(mIsTransientError.get());
mAuthTask.onFailure(mAuthError);
} else {
// Transient error with tries left; register for another attempt.
NetworkChangeNotifier.addConnectionTypeObserver(ConnectionRetry.this);

@ -39,6 +39,8 @@ import org.chromium.build.annotations.Nullable;
import org.chromium.components.externalauth.ExternalAuthUtils;
import org.chromium.components.signin.base.GaiaId;
import org.chromium.components.signin.metrics.FetchAccountCapabilitiesFromSystemLibraryResult;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import java.io.IOException;
@ -123,11 +125,15 @@ public class SystemAccountManagerDelegate implements AccountManagerDelegate {
// This case includes a UserRecoverableNotifiedException, but most clients will have
// their own retry mechanism anyway.
throw new AuthException(
AuthException.NONTRANSIENT,
"Error while getting token for scope '" + authTokenScope + "'",
ex);
ex,
new GoogleServiceAuthError(
GoogleServiceAuthErrorState.INVALID_GAIA_CREDENTIALS));
} catch (IOException ex) {
throw new AuthException(AuthException.TRANSIENT, ex);
throw new AuthException(
"Error while getting token for scope '" + authTokenScope + "'",
ex,
new GoogleServiceAuthError(GoogleServiceAuthErrorState.CONNECTION_FAILED));
}
}
@ -136,9 +142,16 @@ public class SystemAccountManagerDelegate implements AccountManagerDelegate {
try {
GoogleAuthUtil.clearToken(ContextUtils.getApplicationContext(), authToken);
} catch (GoogleAuthException ex) {
throw new AuthException(AuthException.NONTRANSIENT, ex);
throw new AuthException(
"Error while invalidating access token",
ex,
new GoogleServiceAuthError(
GoogleServiceAuthErrorState.INVALID_GAIA_CREDENTIALS));
} catch (IOException ex) {
throw new AuthException(AuthException.TRANSIENT, ex);
throw new AuthException(
"Error while invalidating access token",
ex,
new GoogleServiceAuthError(GoogleServiceAuthErrorState.CONNECTION_FAILED));
}
}

@ -8,6 +8,7 @@ import androidx.annotation.MainThread;
import androidx.annotation.VisibleForTesting;
import org.jni_zero.CalledByNative;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;
import org.chromium.base.Promise;
@ -19,6 +20,8 @@ import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.AccountUtils;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import java.util.List;
@ -60,51 +63,56 @@ final class ProfileOAuth2TokenServiceDelegate {
.getCoreAccountInfos()
.then(
coreAccountInfos -> {
final CoreAccountInfo coreAccountInfo =
final @Nullable CoreAccountInfo coreAccountInfo =
AccountUtils.findCoreAccountInfoByEmail(
coreAccountInfos, accountEmail);
if (coreAccountInfo == null) {
ThreadUtils.postOnUiThread(
() -> {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
null,
AccessTokenData
.NO_KNOWN_EXPIRATION_TIME,
false,
nativeCallback);
});
return;
}
String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
mAccountManagerFacade.getAccessToken(
coreAccountInfo,
oauth2Scope,
new AccountManagerFacade.GetAccessTokenCallback() {
@Override
public void onGetTokenSuccess(AccessTokenData token) {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
token.getToken(),
token.getExpirationTimeSecs(),
false,
nativeCallback);
}
@Override
public void onGetTokenFailure(boolean isTransientError) {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
null,
AccessTokenData
.NO_KNOWN_EXPIRATION_TIME,
isTransientError,
nativeCallback);
}
});
getAccessToken(coreAccountInfo, scope, nativeCallback);
});
}
private void getAccessToken(
@Nullable CoreAccountInfo coreAccountInfo, String scope, final long nativeCallback) {
if (coreAccountInfo == null) {
ThreadUtils.postOnUiThread(
() -> {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
null,
AccessTokenData.NO_KNOWN_EXPIRATION_TIME,
new GoogleServiceAuthError(
GoogleServiceAuthErrorState.REQUEST_CANCELED),
nativeCallback);
});
return;
}
String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
mAccountManagerFacade.getAccessToken(
coreAccountInfo,
oauth2Scope,
new AccountManagerFacade.GetAccessTokenCallback() {
@Override
public void onGetTokenSuccess(AccessTokenData token) {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
token.getToken(),
token.getExpirationTimeSecs(),
new GoogleServiceAuthError(
GoogleServiceAuthErrorState.NONE),
nativeCallback);
}
@Override
public void onGetTokenFailure(GoogleServiceAuthError authError) {
ProfileOAuth2TokenServiceDelegateJni.get()
.onOAuth2TokenFetched(
null,
AccessTokenData.NO_KNOWN_EXPIRATION_TIME,
authError,
nativeCallback);
}
});
}
/**
* Called by native to invalidate an OAuth2 token. Please note that the token is invalidated
* asynchronously.
@ -138,15 +146,15 @@ final class ProfileOAuth2TokenServiceDelegate {
* @param authToken The string value of the OAuth2 token.
* @param expirationTimeSecs The number of seconds after the Unix epoch when the token is
* scheduled to expire. It is set to 0 if there's no known expiration time.
* @param isTransientError Indicates if the error is transient (network timeout or *
* unavailable, etc) or persistent (bad credentials, permission denied, etc).
* @param authError The {@link GoogleServiceAuthError} encountered during token fetch. Not
* checked if authToken is not null.
* @param nativeCallback the pointer to the native callback that should be run upon
* completion.
*/
void onOAuth2TokenFetched(
@Nullable String authToken,
long expirationTimeSecs,
boolean isTransientError,
@JniType("GoogleServiceAuthError") GoogleServiceAuthError authError,
long nativeCallback);
}
}

@ -21,6 +21,8 @@ import org.chromium.components.signin.AuthException;
import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountId;
import org.chromium.components.signin.base.GaiaId;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -98,8 +100,10 @@ public class FakeAccountManagerDelegate implements AccountManagerDelegate {
AccountHolder accountHolder = tryGetAccountHolder(account.name);
if (accountHolder == null) {
throw new AuthException(
AuthException.NONTRANSIENT,
"Cannot get auth token for unknown account '" + account + "'");
"Error while getting token for scope '" + scope + "'",
new IllegalStateException(
"Cannot get auth token for unknown account '" + account + "'"),
new GoogleServiceAuthError(GoogleServiceAuthErrorState.USER_NOT_SIGNED_UP));
}
return accountHolder.getAccessTokenOrGenerateNew(scope);
}

@ -30,6 +30,8 @@ import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountId;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.base.GaiaId;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import org.chromium.google_apis.gaia.GoogleServiceAuthErrorState;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -168,7 +170,10 @@ public class FakeAccountManagerFacade implements AccountManagerFacade {
if (accountHolder == null) {
Log.w(TAG, "Cannot find account:" + coreAccountInfo.toString());
ThreadUtils.runOnUiThread(
() -> callback.onGetTokenFailure(/* isTransientError= */ false));
() ->
callback.onGetTokenFailure(
new GoogleServiceAuthError(
GoogleServiceAuthErrorState.USER_NOT_SIGNED_UP)));
return;
}
ThreadUtils.runOnUiThread(

@ -28,6 +28,7 @@ import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
import org.chromium.components.signin.test.util.TestAccounts;
import org.chromium.google_apis.gaia.GoogleServiceAuthError;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -115,7 +116,7 @@ public class AccountManagerFacadeTest {
}
@Override
public void onGetTokenFailure(boolean isTransientError) {
public void onGetTokenFailure(GoogleServiceAuthError authError) {
mToken = null;
mTokenRetrievedCountDown.countDown();
}

@ -5,6 +5,7 @@
package org.chromium.google_apis.gaia;
import org.jni_zero.CalledByNative;
import org.jni_zero.NativeMethods;
import org.chromium.build.annotations.NullMarked;
@ -25,4 +26,13 @@ public class GoogleServiceAuthError {
public @GoogleServiceAuthErrorState int getState() {
return mState;
}
public boolean isTransientError() {
return GoogleServiceAuthErrorJni.get().isTransientError(mState);
}
@NativeMethods
interface Natives {
boolean isTransientError(@GoogleServiceAuthErrorState int state);
}
}

@ -55,6 +55,20 @@ const char* ScopeLimitedUnrecoverableErrorReasonToString(
}
NOTREACHED();
}
bool IsTransientError(GoogleServiceAuthError::State state) {
switch (state) {
// These are failures that are likely to succeed if tried again.
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
case GoogleServiceAuthError::REQUEST_CANCELED:
case GoogleServiceAuthError::CHALLENGE_RESPONSE_REQUIRED:
return true;
// Everything else will have the same result.
default:
return false;
}
}
} // namespace
bool GoogleServiceAuthError::operator==(
@ -260,17 +274,7 @@ bool GoogleServiceAuthError::IsScopePersistentError() const {
}
bool GoogleServiceAuthError::IsTransientError() const {
switch (state_) {
// These are failures that are likely to succeed if tried again.
case GoogleServiceAuthError::CONNECTION_FAILED:
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
case GoogleServiceAuthError::REQUEST_CANCELED:
case GoogleServiceAuthError::CHALLENGE_RESPONSE_REQUIRED:
return true;
// Everything else will have the same result.
default:
return false;
}
return ::IsTransientError(state_);
}
#if BUILDFLAG(IS_ANDROID)
@ -298,4 +302,8 @@ jni_zero::ScopedJavaLocalRef<jobject> GoogleServiceAuthError::ToJavaObject(
JNIEnv* env) const {
return Java_GoogleServiceAuthError_Constructor(env, state_);
}
jboolean JNI_GoogleServiceAuthError_IsTransientError(JNIEnv* env, jint state) {
return IsTransientError(static_cast<GoogleServiceAuthError::State>(state));
}
#endif // BUILDFLAG(IS_ANDROID)