- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / signin / OAuth2TokenService.java
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.
4
5 package org.chromium.chrome.browser.signin;
6
7 import android.accounts.Account;
8 import android.app.Activity;
9 import android.content.Context;
10 import android.util.Log;
11
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;
18
19 import java.util.concurrent.Semaphore;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.atomic.AtomicReference;
22
23 import javax.annotation.Nullable;
24
25 /**
26  * Java instance for the native OAuth2TokenService.
27  * <p/>
28  * This class forwards calls to request or invalidate access tokens made by native code to
29  * AccountManagerHelper and forwards callbacks to native code.
30  * <p/>
31  */
32 public final class OAuth2TokenService {
33
34     private static final String TAG = "OAuth2TokenService";
35
36     public interface OAuth2TokenServiceObserver {
37         void onRefreshTokenAvailable(Account account);
38         void onRefreshTokenRevoked(Account account);
39         void onRefreshTokensLoaded();
40     }
41
42     private static final String OAUTH2_SCOPE_PREFIX = "oauth2:";
43
44     private final int mNativeProfileOAuth2TokenService;
45     private final ObserverList<OAuth2TokenServiceObserver> mObservers;
46
47     private OAuth2TokenService(int nativeOAuth2Service) {
48         mNativeProfileOAuth2TokenService = nativeOAuth2Service;
49         mObservers = new ObserverList<OAuth2TokenServiceObserver>();
50     }
51
52     public static OAuth2TokenService getForProfile(Profile profile) {
53         ThreadUtils.assertOnUiThread();
54         return (OAuth2TokenService) nativeGetForProfile(profile);
55     }
56
57     @CalledByNative
58     private static OAuth2TokenService create(int nativeOAuth2Service) {
59         ThreadUtils.assertOnUiThread();
60         return new OAuth2TokenService(nativeOAuth2Service);
61     }
62
63     public void addObserver(OAuth2TokenServiceObserver observer) {
64         ThreadUtils.assertOnUiThread();
65         mObservers.addObserver(observer);
66     }
67
68     public void removeObserver(OAuth2TokenServiceObserver observer) {
69         ThreadUtils.assertOnUiThread();
70         mObservers.removeObserver(observer);
71     }
72
73     private static Account getAccountOrNullFromUsername(Context context, String username) {
74         if (username == null) {
75             Log.e(TAG, "Username is null");
76             return null;
77         }
78
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.");
83             return null;
84         }
85         return account;
86     }
87
88     /**
89      * Called by native to list the accounts with OAuth2 refresh tokens.
90      */
91     @CalledByNative
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()]);
96     }
97
98     /**
99      * Called by native to retrieve OAuth2 tokens.
100      *
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.
104      */
105     @CalledByNative
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);
111             return;
112         }
113         String oauth2Scope = OAUTH2_SCOPE_PREFIX + scope;
114
115         AccountManagerHelper accountManagerHelper = AccountManagerHelper.get(context);
116         accountManagerHelper.getAuthTokenFromForeground(
117             null, account, oauth2Scope, new AccountManagerHelper.GetAuthTokenCallback() {
118                 @Override
119                 public void tokenAvailable(String token) {
120                     nativeOAuth2TokenFetched(
121                         token, token != null, nativeCallback);
122                 }
123             });
124     }
125
126     /**
127      * Call this method to retrieve an OAuth2 access token for the given account and scope.
128      *
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.
133      */
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);
140     }
141
142     /**
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.
145      *
146      * Given that this is a blocking method call, this should never be called from the UI thread.
147      *
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|.
153      */
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() {
163                     @Override
164                     public void tokenAvailable(String token) {
165                         result.set(token);
166                         semaphore.release();
167                     }
168                 });
169         try {
170             if (semaphore.tryAcquire(timeout, unit)) {
171                 return result.get();
172             } else {
173                 Log.d(TAG, "Failed to retrieve auth token within timeout (" +
174                         timeout + " + " + unit.name() + ")");
175                 return null;
176             }
177         } catch (InterruptedException e) {
178             Log.w(TAG, "Got interrupted while waiting for auth token");
179             return null;
180         }
181     }
182
183     /**
184      * Called by native to check wether the account has an OAuth2 refresh token.
185      */
186     @CalledByNative
187     public static boolean hasOAuth2RefreshToken(Context context, String accountName) {
188         return AccountManagerHelper.get(context).hasAccountForName(accountName);
189     }
190
191     /**
192     * Called by native to invalidate an OAuth2 token.
193     */
194     @CalledByNative
195     public static void invalidateOAuth2AuthToken(Context context, String accessToken) {
196         if (accessToken != null) {
197             AccountManagerHelper.get(context).invalidateAuthToken(accessToken);
198         }
199     }
200
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);
208     }
209
210     /**
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.
214      */
215     public void fireRefreshTokenAvailable(Account account) {
216         ThreadUtils.assertOnUiThread();
217         assert account != null;
218         nativeFireRefreshTokenAvailableFromJava(mNativeProfileOAuth2TokenService, account.name);
219     }
220
221     @CalledByNative
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);
227         }
228     }
229
230     /**
231      * Triggers a notification to all observers of the native and Java instance of the
232      * OAuth2TokenService that a refresh token is now revoked.
233      */
234     public void fireRefreshTokenRevoked(Account account) {
235         ThreadUtils.assertOnUiThread();
236         assert account != null;
237         nativeFireRefreshTokenRevokedFromJava(mNativeProfileOAuth2TokenService, account.name);
238     }
239
240     @CalledByNative
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);
246         }
247     }
248
249     /**
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.
252      */
253     public void fireRefreshTokensLoaded() {
254         ThreadUtils.assertOnUiThread();
255         nativeFireRefreshTokensLoadedFromJava(mNativeProfileOAuth2TokenService);
256     }
257
258     @CalledByNative
259     public void notifyRefreshTokensLoaded() {
260         for (OAuth2TokenServiceObserver observer : mObservers) {
261             observer.onRefreshTokensLoaded();
262         }
263     }
264
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);
276 }