[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:

committed by
Chromium LUCI CQ

parent
b3e92d0218
commit
6749bc6fac
components/signin
google_apis/gaia
@ -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",
|
||||
|
6
components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
6
components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.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)
|
||||
|
Reference in New Issue
Block a user