1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.chrome.browser.signin;
7 import android.accounts.Account;
8 import android.app.Activity;
9 import android.content.Context;
10 import android.util.Log;
12 import org.chromium.base.CalledByNative;
13 import org.chromium.base.ObserverList;
14 import org.chromium.base.ThreadUtils;
15 import org.chromium.chrome.browser.profiles.Profile;
16 import org.chromium.sync.signin.AccountManagerHelper;
17 import org.chromium.sync.signin.ChromeSigninController;
19 import java.util.concurrent.Semaphore;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.atomic.AtomicReference;
23 import javax.annotation.Nullable;
26 * Java instance for the native OAuth2TokenService.
28 * This class forwards calls to request or invalidate access tokens made by native code to
29 * AccountManagerHelper and forwards callbacks to native code.
32 public final class OAuth2TokenService {
34 private static final String TAG = "OAuth2TokenService";
36 public interface OAuth2TokenServiceObserver {
37 void onRefreshTokenAvailable(Account account);
38 void onRefreshTokenRevoked(Account account);
39 void onRefreshTokensLoaded();
42 private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
44 private final int mNativeProfileOAuth2TokenService;
45 private final ObserverList<OAuth2TokenServiceObserver> mObservers;
47 private OAuth2TokenService(int nativeOAuth2Service) {
48 mNativeProfileOAuth2TokenService = nativeOAuth2Service;
49 mObservers = new ObserverList<OAuth2TokenServiceObserver>();
52 public static OAuth2TokenService getForProfile(Profile profile) {
53 ThreadUtils.assertOnUiThread();
54 return (OAuth2TokenService) nativeGetForProfile(profile);
58 private static OAuth2TokenService create(int nativeOAuth2Service) {
59 ThreadUtils.assertOnUiThread();
60 return new OAuth2TokenService(nativeOAuth2Service);
63 public void addObserver(OAuth2TokenServiceObserver observer) {
64 ThreadUtils.assertOnUiThread();
65 mObservers.addObserver(observer);
68 public void removeObserver(OAuth2TokenServiceObserver observer) {
69 ThreadUtils.assertOnUiThread();
70 mObservers.removeObserver(observer);
73 private static Account getAccountOrNullFromUsername(Context context, String username) {
74 if (username == null) {
75 Log.e(TAG, "Username is null");
79 AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
80 Account account = accountManagerHelper.getAccountFromName(username);
81 if (account == null) {
82 Log.e(TAG, "Account not found for provided username.");
89 * Called by native to list the accounts with OAuth2 refresh tokens.
92 public static String[] getAccounts(Context context) {
93 AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
94 java.util.List<String> accountNames = accountManagerHelper.getGoogleAccountNames();
95 return accountNames.toArray(new String[accountNames.size()]);
99 * Called by native to retrieve OAuth2 tokens.
101 * @param username The native username (full address).
102 * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
103 * @param nativeCallback The pointer to the native callback that should be run upon completion.
106 public static void getOAuth2AuthToken(
107 Context context, String username, String scope, final int nativeCallback) {
108 Account account = getAccountOrNullFromUsername(context, username);
109 if (account == null) {
110 nativeOAuth2TokenFetched(null, false, nativeCallback);
113 String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
115 AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
116 accountManagerHelper.getAuthTokenFromForeground(
117 null, account, oauth2Scope, new AccountManagerHelper.GetAuthTokenCallback() {
119 public void tokenAvailable(String token) {
120 nativeOAuth2TokenFetched(
121 token, token != null, nativeCallback);
127 * Call this method to retrieve an OAuth2 access token for the given account and scope.
129 * @param activity the current activity. May be null.
130 * @param account the account to get the access token for.
131 * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
132 * @param callback called on successful and unsuccessful fetching of auth token.
134 public static void getOAuth2AccessToken(Context context, @Nullable Activity activity,
135 Account account, String scope,
136 AccountManagerHelper.GetAuthTokenCallback callback) {
137 String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
138 AccountManagerHelper.get(context).getAuthTokenFromForeground(
139 activity, account, oauth2Scope, callback);
143 * Call this method to retrieve an OAuth2 access token for the given account and scope. This
144 * method times out after the specified timeout, and will return null if that happens.
146 * Given that this is a blocking method call, this should never be called from the UI thread.
148 * @param activity the current activity. May be null.
149 * @param account the account to get the access token for.
150 * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix).
151 * @param timeout the timeout.
152 * @param unit the unit for |timeout|.
154 public static String getOAuth2AccessTokenWithTimeout(
155 Context context, @Nullable Activity activity, Account account, String scope,
156 long timeout, TimeUnit unit) {
157 assert !ThreadUtils.runningOnUiThread();
158 final AtomicReference<String> result = new AtomicReference<String>();
159 final Semaphore semaphore = new Semaphore(0);
160 getOAuth2AccessToken(
161 context, activity, account, scope,
162 new AccountManagerHelper.GetAuthTokenCallback() {
164 public void tokenAvailable(String token) {
170 if (semaphore.tryAcquire(timeout, unit)) {
173 Log.d(TAG, "Failed to retrieve auth token within timeout (" +
174 timeout + " + " + unit.name() + ")");
177 } catch (InterruptedException e) {
178 Log.w(TAG, "Got interrupted while waiting for auth token");
184 * Called by native to check wether the account has an OAuth2 refresh token.
187 public static boolean hasOAuth2RefreshToken(Context context, String accountName) {
188 return AccountManagerHelper.get(context).hasAccountForName(accountName);
192 * Called by native to invalidate an OAuth2 token.
195 public static void invalidateOAuth2AuthToken(Context context, String accessToken) {
196 if (accessToken != null) {
197 AccountManagerHelper.get(context).invalidateAuthToken(accessToken);
201 public void validateAccounts(Context context) {
202 ThreadUtils.assertOnUiThread();
203 String currentlySignedInAccount =
204 ChromeSigninController.get(context).getSignedInAccountName();
205 String[] accounts = getAccounts(context);
206 nativeValidateAccounts(
207 mNativeProfileOAuth2TokenService, accounts, currentlySignedInAccount);
211 * Triggers a notification to all observers of the native and Java instance of the
212 * OAuth2TokenService that a refresh token is now available. This may cause observers to retry
213 * operations that require authentication.
215 public void fireRefreshTokenAvailable(Account account) {
216 ThreadUtils.assertOnUiThread();
217 assert account != null;
218 nativeFireRefreshTokenAvailableFromJava(mNativeProfileOAuth2TokenService, account.name);
222 public void notifyRefreshTokenAvailable(String accountName) {
223 assert accountName != null;
224 Account account = AccountManagerHelper.createAccountFromName(accountName);
225 for (OAuth2TokenServiceObserver observer : mObservers) {
226 observer.onRefreshTokenAvailable(account);
231 * Triggers a notification to all observers of the native and Java instance of the
232 * OAuth2TokenService that a refresh token is now revoked.
234 public void fireRefreshTokenRevoked(Account account) {
235 ThreadUtils.assertOnUiThread();
236 assert account != null;
237 nativeFireRefreshTokenRevokedFromJava(mNativeProfileOAuth2TokenService, account.name);
241 public void notifyRefreshTokenRevoked(String accountName) {
242 assert accountName != null;
243 Account account = AccountManagerHelper.createAccountFromName(accountName);
244 for (OAuth2TokenServiceObserver observer : mObservers) {
245 observer.onRefreshTokenRevoked(account);
250 * Triggers a notification to all observers of the native and Java instance of the
251 * OAuth2TokenService that all refresh tokens now have been loaded.
253 public void fireRefreshTokensLoaded() {
254 ThreadUtils.assertOnUiThread();
255 nativeFireRefreshTokensLoadedFromJava(mNativeProfileOAuth2TokenService);
259 public void notifyRefreshTokensLoaded() {
260 for (OAuth2TokenServiceObserver observer : mObservers) {
261 observer.onRefreshTokensLoaded();
265 private static native Object nativeGetForProfile(Profile profile);
266 private static native void nativeOAuth2TokenFetched(
267 String authToken, boolean result, int nativeCallback);
268 private native void nativeValidateAccounts(int nativeAndroidProfileOAuth2TokenService,
269 String[] accounts, String currentlySignedInAccount);
270 private native void nativeFireRefreshTokenAvailableFromJava(
271 int nativeAndroidProfileOAuth2TokenService, String accountName);
272 private native void nativeFireRefreshTokenRevokedFromJava(
273 int nativeAndroidProfileOAuth2TokenService, String accountName);
274 private native void nativeFireRefreshTokensLoadedFromJava(
275 int nativeAndroidProfileOAuth2TokenService);