Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / android_profile_oauth2_token_service.cc
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 #include "chrome/browser/signin/android_profile_oauth2_token_service.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "chrome/browser/profiles/profile_android.h"
13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14 #include "chrome/browser/sync/profile_sync_service_android.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "google_apis/gaia/gaia_auth_util.h"
17 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
18 #include "jni/OAuth2TokenService_jni.h"
19
20 using base::android::AttachCurrentThread;
21 using base::android::ConvertJavaStringToUTF8;
22 using base::android::ConvertUTF8ToJavaString;
23 using base::android::ScopedJavaLocalRef;
24 using content::BrowserThread;
25
26 namespace {
27
28 // Callback from FetchOAuth2TokenWithUsername().
29 // Arguments:
30 // - the error, or NONE if the token fetch was successful.
31 // - the OAuth2 access token.
32 // - the expiry time of the token (may be null, indicating that the expiry
33 //   time is unknown.
34 typedef base::Callback<void(
35     const GoogleServiceAuthError&, const std::string&, const base::Time&)>
36         FetchOAuth2TokenCallback;
37
38 class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher {
39  public:
40   AndroidAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
41                             const std::string& account_id);
42   virtual ~AndroidAccessTokenFetcher();
43
44   // Overrides from OAuth2AccessTokenFetcher:
45   virtual void Start(const std::string& client_id,
46                      const std::string& client_secret,
47                      const std::vector<std::string>& scopes) override;
48   virtual void CancelRequest() override;
49
50   // Handles an access token response.
51   void OnAccessTokenResponse(const GoogleServiceAuthError& error,
52                              const std::string& access_token,
53                              const base::Time& expiration_time);
54
55  private:
56   std::string CombineScopes(const std::vector<std::string>& scopes);
57
58   base::WeakPtrFactory<AndroidAccessTokenFetcher> weak_factory_;
59   std::string account_id_;
60   bool request_was_cancelled_;
61
62   DISALLOW_COPY_AND_ASSIGN(AndroidAccessTokenFetcher);
63 };
64
65 AndroidAccessTokenFetcher::AndroidAccessTokenFetcher(
66     OAuth2AccessTokenConsumer* consumer,
67     const std::string& account_id)
68     : OAuth2AccessTokenFetcher(consumer),
69       weak_factory_(this),
70       account_id_(account_id),
71       request_was_cancelled_(false) {
72 }
73
74 AndroidAccessTokenFetcher::~AndroidAccessTokenFetcher() {}
75
76 void AndroidAccessTokenFetcher::Start(const std::string& client_id,
77                                       const std::string& client_secret,
78                                       const std::vector<std::string>& scopes) {
79   JNIEnv* env = AttachCurrentThread();
80   std::string scope = CombineScopes(scopes);
81   ScopedJavaLocalRef<jstring> j_username =
82       ConvertUTF8ToJavaString(env, account_id_);
83   ScopedJavaLocalRef<jstring> j_scope =
84       ConvertUTF8ToJavaString(env, scope);
85   scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
86       new FetchOAuth2TokenCallback(
87           base::Bind(&AndroidAccessTokenFetcher::OnAccessTokenResponse,
88                      weak_factory_.GetWeakPtr())));
89
90   // Call into Java to get a new token.
91   Java_OAuth2TokenService_getOAuth2AuthToken(
92       env, base::android::GetApplicationContext(),
93       j_username.obj(),
94       j_scope.obj(),
95       reinterpret_cast<intptr_t>(heap_callback.release()));
96 }
97
98 void AndroidAccessTokenFetcher::CancelRequest() {
99   request_was_cancelled_ = true;
100 }
101
102 void AndroidAccessTokenFetcher::OnAccessTokenResponse(
103     const GoogleServiceAuthError& error,
104     const std::string& access_token,
105     const base::Time& expiration_time) {
106   if (request_was_cancelled_) {
107     // Ignore the callback if the request was cancelled.
108     return;
109   }
110   if (error.state() == GoogleServiceAuthError::NONE) {
111     FireOnGetTokenSuccess(access_token, expiration_time);
112   } else {
113     FireOnGetTokenFailure(error);
114   }
115 }
116
117 // static
118 std::string AndroidAccessTokenFetcher::CombineScopes(
119     const std::vector<std::string>& scopes) {
120   // The Android AccountManager supports multiple scopes separated by a space:
121   // https://code.google.com/p/google-api-java-client/wiki/OAuth2#Android
122   std::string scope;
123   for (std::vector<std::string>::const_iterator it = scopes.begin();
124        it != scopes.end(); ++it) {
125     if (!scope.empty())
126       scope += " ";
127     scope += *it;
128   }
129   return scope;
130 }
131
132 }  // namespace
133
134 bool AndroidProfileOAuth2TokenService::is_testing_profile_ = false;
135
136 AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService() {
137   DVLOG(1) << "AndroidProfileOAuth2TokenService::ctor";
138   JNIEnv* env = AttachCurrentThread();
139   base::android::ScopedJavaLocalRef<jobject> local_java_ref =
140       Java_OAuth2TokenService_create(env, reinterpret_cast<intptr_t>(this));
141   java_ref_.Reset(env, local_java_ref.obj());
142 }
143
144 AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
145
146 // static
147 jobject AndroidProfileOAuth2TokenService::GetForProfile(
148     JNIEnv* env, jclass clazz, jobject j_profile_android) {
149   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
150   AndroidProfileOAuth2TokenService* service =
151       ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(profile);
152   return service->java_ref_.obj();
153 }
154
155 static jobject GetForProfile(JNIEnv* env,
156                              jclass clazz,
157                              jobject j_profile_android) {
158   return AndroidProfileOAuth2TokenService::GetForProfile(
159       env, clazz, j_profile_android);
160 }
161
162 void AndroidProfileOAuth2TokenService::Initialize(SigninClient* client) {
163   DVLOG(1) << "AndroidProfileOAuth2TokenService::Initialize";
164   ProfileOAuth2TokenService::Initialize(client);
165
166   if (!is_testing_profile_) {
167     Java_OAuth2TokenService_validateAccounts(
168         AttachCurrentThread(), java_ref_.obj(),
169         base::android::GetApplicationContext(), JNI_TRUE);
170   }
171 }
172
173 bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable(
174     const std::string& account_id) const {
175   JNIEnv* env = AttachCurrentThread();
176   ScopedJavaLocalRef<jstring> j_account_id =
177       ConvertUTF8ToJavaString(env, account_id);
178   jboolean refresh_token_is_available =
179       Java_OAuth2TokenService_hasOAuth2RefreshToken(
180           env, base::android::GetApplicationContext(),
181           j_account_id.obj());
182   return refresh_token_is_available == JNI_TRUE;
183 }
184
185 void AndroidProfileOAuth2TokenService::UpdateAuthError(
186     const std::string& account_id,
187     const GoogleServiceAuthError& error) {
188   // TODO(rogerta): do we need to update anything, or does the system handle it?
189 }
190
191 std::vector<std::string> AndroidProfileOAuth2TokenService::GetAccounts() {
192   std::vector<std::string> accounts;
193   JNIEnv* env = AttachCurrentThread();
194   ScopedJavaLocalRef<jobjectArray> j_accounts =
195       Java_OAuth2TokenService_getAccounts(
196           env, base::android::GetApplicationContext());
197   // TODO(fgorski): We may decide to filter out some of the accounts.
198   base::android::AppendJavaStringArrayToStringVector(env,
199                                                      j_accounts.obj(),
200                                                      &accounts);
201   return accounts;
202 }
203
204 std::vector<std::string> AndroidProfileOAuth2TokenService::GetSystemAccounts() {
205   std::vector<std::string> accounts;
206   JNIEnv* env = AttachCurrentThread();
207   ScopedJavaLocalRef<jobjectArray> j_accounts =
208       Java_OAuth2TokenService_getSystemAccounts(
209           env, base::android::GetApplicationContext());
210   base::android::AppendJavaStringArrayToStringVector(env,
211                                                      j_accounts.obj(),
212                                                      &accounts);
213   return accounts;
214 }
215
216 OAuth2AccessTokenFetcher*
217 AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher(
218     const std::string& account_id,
219     net::URLRequestContextGetter* getter,
220     OAuth2AccessTokenConsumer* consumer) {
221   ValidateAccountId(account_id);
222   return new AndroidAccessTokenFetcher(consumer, account_id);
223 }
224
225 void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token(
226     const std::string& account_id,
227     const std::string& client_id,
228     const ScopeSet& scopes,
229     const std::string& access_token) {
230   ValidateAccountId(account_id);
231   OAuth2TokenService::InvalidateOAuth2Token(account_id,
232                                             client_id,
233                                             scopes,
234                                             access_token);
235
236   JNIEnv* env = AttachCurrentThread();
237   ScopedJavaLocalRef<jstring> j_access_token =
238       ConvertUTF8ToJavaString(env, access_token);
239   Java_OAuth2TokenService_invalidateOAuth2AuthToken(
240       env, base::android::GetApplicationContext(),
241       j_access_token.obj());
242 }
243
244 void AndroidProfileOAuth2TokenService::ValidateAccounts(
245     JNIEnv* env,
246     jobject obj,
247     jstring j_current_acc,
248     jboolean j_force_notifications) {
249   DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts from java";
250   std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc);
251   if (!signed_in_account.empty())
252     signed_in_account = gaia::CanonicalizeEmail(signed_in_account);
253   ValidateAccounts(signed_in_account, j_force_notifications != JNI_FALSE);
254 }
255
256 void AndroidProfileOAuth2TokenService::ValidateAccounts(
257     const std::string& signed_in_account,
258     bool force_notifications) {
259   std::vector<std::string> prev_ids = GetAccounts();
260   std::vector<std::string> curr_ids = GetSystemAccounts();
261   std::vector<std::string> refreshed_ids;
262   std::vector<std::string> revoked_ids;
263
264   // Canonicalize system accounts.  |prev_ids| is already done.
265   for (size_t i = 0; i < curr_ids.size(); ++i)
266     curr_ids[i] = gaia::CanonicalizeEmail(curr_ids[i]);
267   for (size_t i = 0; i < prev_ids.size(); ++i)
268     ValidateAccountId(prev_ids[i]);
269
270   DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
271            << " sigined_in_account=" << signed_in_account
272            << " prev_ids=" << prev_ids.size()
273            << " curr_ids=" << curr_ids.size()
274            << " force=" << (force_notifications ? "true" : "false");
275
276   if (!ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids,
277                         revoked_ids, force_notifications)) {
278     curr_ids.clear();
279   }
280
281   ScopedBatchChange batch(this);
282
283   JNIEnv* env = AttachCurrentThread();
284   ScopedJavaLocalRef<jobjectArray> java_accounts(
285       base::android::ToJavaArrayOfStrings(env, curr_ids));
286   Java_OAuth2TokenService_saveStoredAccounts(
287       env, base::android::GetApplicationContext(), java_accounts.obj());
288
289   for (std::vector<std::string>::iterator it = refreshed_ids.begin();
290        it != refreshed_ids.end(); it++) {
291     FireRefreshTokenAvailable(*it);
292   }
293
294   for (std::vector<std::string>::iterator it = revoked_ids.begin();
295        it != revoked_ids.end(); it++) {
296     FireRefreshTokenRevoked(*it);
297   }
298 }
299
300 bool AndroidProfileOAuth2TokenService::ValidateAccounts(
301     const std::string& signed_in_account,
302     const std::vector<std::string>& prev_account_ids,
303     const std::vector<std::string>& curr_account_ids,
304     std::vector<std::string>& refreshed_ids,
305     std::vector<std::string>& revoked_ids,
306     bool force_notifications) {
307   if (std::find(curr_account_ids.begin(),
308                 curr_account_ids.end(),
309                 signed_in_account) != curr_account_ids.end()) {
310     // Test to see if an account is removed from the Android AccountManager.
311     // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
312     for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
313          it != prev_account_ids.end(); it++) {
314       if (*it == signed_in_account)
315         continue;
316
317       if (std::find(curr_account_ids.begin(),
318                     curr_account_ids.end(),
319                     *it) == curr_account_ids.end()) {
320         DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
321                  << "revoked=" << *it;
322         revoked_ids.push_back(*it);
323       }
324     }
325
326     if (force_notifications ||
327         std::find(prev_account_ids.begin(), prev_account_ids.end(),
328                   signed_in_account) == prev_account_ids.end()) {
329       // Always fire the primary signed in account first.
330       DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
331                << "refreshed=" << signed_in_account;
332       refreshed_ids.push_back(signed_in_account);
333     }
334
335     for (std::vector<std::string>::const_iterator it = curr_account_ids.begin();
336          it != curr_account_ids.end(); it++) {
337       if (*it != signed_in_account) {
338         if (force_notifications ||
339             std::find(prev_account_ids.begin(),
340                       prev_account_ids.end(),
341                       *it) == prev_account_ids.end()) {
342           DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
343                    << "refreshed=" << *it;
344           refreshed_ids.push_back(*it);
345         }
346       }
347     }
348     return true;
349   } else {
350     // Currently signed in account does not any longer exist among accounts on
351     // system together with all other accounts.
352     if (std::find(prev_account_ids.begin(), prev_account_ids.end(),
353                   signed_in_account) != prev_account_ids.end()) {
354       DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
355                << "revoked=" << signed_in_account;
356       revoked_ids.push_back(signed_in_account);
357     }
358     for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
359          it != prev_account_ids.end(); it++) {
360       if (*it == signed_in_account)
361         continue;
362       DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
363                << "revoked=" << *it;
364       revoked_ids.push_back(*it);
365     }
366     return false;
367   }
368 }
369
370 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
371     JNIEnv* env,
372     jobject obj,
373     const jstring account_name) {
374   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
375   AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id);
376 }
377
378 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
379     const std::string& account_id) {
380   DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable id="
381            << account_id;
382
383   // Notify native observers.
384   OAuth2TokenService::FireRefreshTokenAvailable(account_id);
385   // Notify Java observers.
386   JNIEnv* env = AttachCurrentThread();
387   ScopedJavaLocalRef<jstring> account_name =
388       ConvertUTF8ToJavaString(env, account_id);
389   Java_OAuth2TokenService_notifyRefreshTokenAvailable(
390       env, java_ref_.obj(), account_name.obj());
391 }
392
393 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
394     JNIEnv* env,
395     jobject obj,
396     const jstring account_name) {
397   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
398   AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id);
399 }
400
401 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
402     const std::string& account_id) {
403   DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked id="
404            << account_id;
405
406   // Notify native observers.
407   OAuth2TokenService::FireRefreshTokenRevoked(account_id);
408   // Notify Java observers.
409   JNIEnv* env = AttachCurrentThread();
410   ScopedJavaLocalRef<jstring> account_name =
411       ConvertUTF8ToJavaString(env, account_id);
412   Java_OAuth2TokenService_notifyRefreshTokenRevoked(
413       env, java_ref_.obj(), account_name.obj());
414 }
415
416 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
417     JNIEnv* env,
418     jobject obj) {
419   AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
420 }
421
422 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() {
423   DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded";
424   // Notify native observers.
425   OAuth2TokenService::FireRefreshTokensLoaded();
426   // Notify Java observers.
427   JNIEnv* env = AttachCurrentThread();
428   Java_OAuth2TokenService_notifyRefreshTokensLoaded(
429       env, java_ref_.obj());
430 }
431
432 void AndroidProfileOAuth2TokenService::RevokeAllCredentials() {
433   DVLOG(1) << "AndroidProfileOAuth2TokenService::RevokeAllCredentials";
434   ScopedBatchChange batch(this);
435   std::vector<std::string> accounts = GetAccounts();
436   for (std::vector<std::string>::iterator it = accounts.begin();
437        it != accounts.end(); it++) {
438     FireRefreshTokenRevoked(*it);
439   }
440
441   // Clear everything on the Java side as well.
442   std::vector<std::string> empty;
443   JNIEnv* env = AttachCurrentThread();
444   ScopedJavaLocalRef<jobjectArray> java_accounts(
445       base::android::ToJavaArrayOfStrings(env, empty));
446   Java_OAuth2TokenService_saveStoredAccounts(
447       env, base::android::GetApplicationContext(), java_accounts.obj());
448 }
449
450 // Called from Java when fetching of an OAuth2 token is finished. The
451 // |authToken| param is only valid when |result| is true.
452 void OAuth2TokenFetched(
453     JNIEnv* env,
454     jclass clazz,
455     jstring authToken,
456     jboolean result,
457     jlong nativeCallback) {
458   std::string token = ConvertJavaStringToUTF8(env, authToken);
459   scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
460       reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
461   // Android does not provide enough information to know if the credentials are
462   // wrong, so assume any error is transient by using CONNECTION_FAILED.
463   GoogleServiceAuthError err(result ?
464                              GoogleServiceAuthError::NONE :
465                              GoogleServiceAuthError::CONNECTION_FAILED);
466   heap_callback->Run(err, token, base::Time());
467 }
468
469 // static
470 bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
471   return RegisterNativesImpl(env);
472 }