MD Downloads: UI review feedback
[chromium-blink-merge.git] / chrome / browser / signin / oauth2_token_service_delegate_android.cc
blobbad1a248d02011d79af966ace2d834648639ac86
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 #include "chrome/browser/signin/oauth2_token_service_delegate_android.h"
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 "components/signin/core/browser/account_info.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "google_apis/gaia/gaia_auth_util.h"
18 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
19 #include "jni/OAuth2TokenService_jni.h"
21 using base::android::AttachCurrentThread;
22 using base::android::ConvertJavaStringToUTF8;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ScopedJavaLocalRef;
25 using content::BrowserThread;
27 namespace {
29 // Callback from FetchOAuth2TokenWithUsername().
30 // Arguments:
31 // - the error, or NONE if the token fetch was successful.
32 // - the OAuth2 access token.
33 // - the expiry time of the token (may be null, indicating that the expiry
34 // time is unknown.
35 typedef base::Callback<void(const GoogleServiceAuthError&,
36 const std::string&,
37 const base::Time&)> FetchOAuth2TokenCallback;
39 class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher {
40 public:
41 AndroidAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
42 const std::string& account_id);
43 ~AndroidAccessTokenFetcher() override;
45 // Overrides from OAuth2AccessTokenFetcher:
46 void Start(const std::string& client_id,
47 const std::string& client_secret,
48 const std::vector<std::string>& scopes) override;
49 void CancelRequest() override;
51 // Handles an access token response.
52 void OnAccessTokenResponse(const GoogleServiceAuthError& error,
53 const std::string& access_token,
54 const base::Time& expiration_time);
56 private:
57 std::string CombineScopes(const std::vector<std::string>& scopes);
59 std::string account_id_;
60 bool request_was_cancelled_;
61 base::WeakPtrFactory<AndroidAccessTokenFetcher> weak_factory_;
63 DISALLOW_COPY_AND_ASSIGN(AndroidAccessTokenFetcher);
66 AndroidAccessTokenFetcher::AndroidAccessTokenFetcher(
67 OAuth2AccessTokenConsumer* consumer,
68 const std::string& account_id)
69 : OAuth2AccessTokenFetcher(consumer),
70 account_id_(account_id),
71 request_was_cancelled_(false),
72 weak_factory_(this) {
75 AndroidAccessTokenFetcher::~AndroidAccessTokenFetcher() {
78 void AndroidAccessTokenFetcher::Start(const std::string& client_id,
79 const std::string& client_secret,
80 const std::vector<std::string>& scopes) {
81 JNIEnv* env = AttachCurrentThread();
82 std::string scope = CombineScopes(scopes);
83 ScopedJavaLocalRef<jstring> j_username =
84 ConvertUTF8ToJavaString(env, account_id_);
85 ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope);
86 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
87 new FetchOAuth2TokenCallback(
88 base::Bind(&AndroidAccessTokenFetcher::OnAccessTokenResponse,
89 weak_factory_.GetWeakPtr())));
91 // Call into Java to get a new token.
92 Java_OAuth2TokenService_getOAuth2AuthToken(
93 env, base::android::GetApplicationContext(), j_username.obj(),
94 j_scope.obj(), reinterpret_cast<intptr_t>(heap_callback.release()));
97 void AndroidAccessTokenFetcher::CancelRequest() {
98 request_was_cancelled_ = true;
101 void AndroidAccessTokenFetcher::OnAccessTokenResponse(
102 const GoogleServiceAuthError& error,
103 const std::string& access_token,
104 const base::Time& expiration_time) {
105 if (request_was_cancelled_) {
106 // Ignore the callback if the request was cancelled.
107 return;
109 if (error.state() == GoogleServiceAuthError::NONE) {
110 FireOnGetTokenSuccess(access_token, expiration_time);
111 } else {
112 FireOnGetTokenFailure(error);
116 // static
117 std::string AndroidAccessTokenFetcher::CombineScopes(
118 const std::vector<std::string>& scopes) {
119 // The Android AccountManager supports multiple scopes separated by a space:
120 // https://code.google.com/p/google-api-java-client/wiki/OAuth2#Android
121 std::string scope;
122 for (std::vector<std::string>::const_iterator it = scopes.begin();
123 it != scopes.end(); ++it) {
124 if (!scope.empty())
125 scope += " ";
126 scope += *it;
128 return scope;
131 } // namespace
133 bool OAuth2TokenServiceDelegateAndroid::is_testing_profile_ = false;
135 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo()
136 : error(GoogleServiceAuthError::NONE) {}
138 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo(
139 const GoogleServiceAuthError& error)
140 : error(error) {}
142 OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid(
143 AccountTrackerService* account_tracker_service)
144 : account_tracker_service_(account_tracker_service),
145 fire_refresh_token_loaded_(RT_LOAD_NOT_START) {
146 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ctor";
147 DCHECK(account_tracker_service_);
148 JNIEnv* env = AttachCurrentThread();
149 base::android::ScopedJavaLocalRef<jobject> local_java_ref =
150 Java_OAuth2TokenService_create(env,
151 base::android::GetApplicationContext(),
152 reinterpret_cast<intptr_t>(this));
153 java_ref_.Reset(env, local_java_ref.obj());
155 if (account_tracker_service_->GetMigrationState() ==
156 AccountTrackerService::MIGRATION_IN_PROGRESS) {
157 std::vector<std::string> accounts = GetAccounts();
158 std::vector<std::string> accounts_id;
159 for (auto account_name : accounts) {
160 AccountInfo account_info =
161 account_tracker_service_->FindAccountInfoByEmail(account_name);
162 DCHECK(!account_info.gaia.empty());
163 accounts_id.push_back(account_info.gaia);
165 ScopedJavaLocalRef<jobjectArray> java_accounts(
166 base::android::ToJavaArrayOfStrings(env, accounts_id));
167 Java_OAuth2TokenService_saveStoredAccounts(
168 env, base::android::GetApplicationContext(), java_accounts.obj());
171 if (!is_testing_profile_) {
172 Java_OAuth2TokenService_validateAccounts(
173 AttachCurrentThread(), java_ref_.obj(),
174 base::android::GetApplicationContext(), JNI_TRUE);
178 OAuth2TokenServiceDelegateAndroid::~OAuth2TokenServiceDelegateAndroid() {
181 // static
182 ScopedJavaLocalRef<jobject> OAuth2TokenServiceDelegateAndroid::GetForProfile(
183 JNIEnv* env,
184 jclass clazz,
185 jobject j_profile_android) {
186 Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
187 ProfileOAuth2TokenService* service =
188 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
189 OAuth2TokenServiceDelegate* delegate = service->GetDelegate();
190 return ScopedJavaLocalRef<jobject>(
191 static_cast<OAuth2TokenServiceDelegateAndroid*>(delegate)->java_ref_);
194 static ScopedJavaLocalRef<jobject> GetForProfile(
195 JNIEnv* env,
196 const JavaParamRef<jclass>& clazz,
197 const JavaParamRef<jobject>& j_profile_android) {
198 return OAuth2TokenServiceDelegateAndroid::GetForProfile(env, clazz,
199 j_profile_android);
202 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable(
203 const std::string& account_id) const {
204 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable"
205 << " account= " << account_id;
206 std::string account_name = MapAccountIdToAccountName(account_id);
207 JNIEnv* env = AttachCurrentThread();
208 ScopedJavaLocalRef<jstring> j_account_id =
209 ConvertUTF8ToJavaString(env, account_name);
210 jboolean refresh_token_is_available =
211 Java_OAuth2TokenService_hasOAuth2RefreshToken(
212 env, base::android::GetApplicationContext(), j_account_id.obj());
213 return refresh_token_is_available == JNI_TRUE;
216 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenHasError(
217 const std::string& account_id) const {
218 auto it = errors_.find(account_id);
219 // TODO(rogerta): should we distinguish between transient and persistent?
220 return it == errors_.end() ? false : IsError(it->second.error);
223 void OAuth2TokenServiceDelegateAndroid::UpdateAuthError(
224 const std::string& account_id,
225 const GoogleServiceAuthError& error) {
226 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError"
227 << " account=" << account_id
228 << " error=" << error.ToString();
229 if (error.state() == GoogleServiceAuthError::NONE) {
230 errors_.erase(account_id);
231 } else {
232 // TODO(rogerta): should we distinguish between transient and persistent?
233 errors_[account_id] = ErrorInfo(error);
237 std::vector<std::string> OAuth2TokenServiceDelegateAndroid::GetAccounts() {
238 std::vector<std::string> accounts;
239 JNIEnv* env = AttachCurrentThread();
240 ScopedJavaLocalRef<jobjectArray> j_accounts =
241 Java_OAuth2TokenService_getAccounts(
242 env, base::android::GetApplicationContext());
243 // TODO(fgorski): We may decide to filter out some of the accounts.
244 base::android::AppendJavaStringArrayToStringVector(env, j_accounts.obj(),
245 &accounts);
246 return accounts;
249 std::vector<std::string>
250 OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() {
251 std::vector<std::string> account_names;
252 JNIEnv* env = AttachCurrentThread();
253 ScopedJavaLocalRef<jobjectArray> j_accounts =
254 Java_OAuth2TokenService_getSystemAccountNames(
255 env, base::android::GetApplicationContext());
256 base::android::AppendJavaStringArrayToStringVector(env, j_accounts.obj(),
257 &account_names);
258 return account_names;
261 OAuth2AccessTokenFetcher*
262 OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher(
263 const std::string& account_id,
264 net::URLRequestContextGetter* getter,
265 OAuth2AccessTokenConsumer* consumer) {
266 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher"
267 << " account= " << account_id;
268 ValidateAccountId(account_id);
269 return new AndroidAccessTokenFetcher(consumer,
270 MapAccountIdToAccountName(account_id));
273 void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken(
274 const std::string& account_id,
275 const std::string& client_id,
276 const OAuth2TokenService::ScopeSet& scopes,
277 const std::string& access_token) {
278 ValidateAccountId(account_id);
279 JNIEnv* env = AttachCurrentThread();
280 ScopedJavaLocalRef<jstring> j_access_token =
281 ConvertUTF8ToJavaString(env, access_token);
282 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
283 env, base::android::GetApplicationContext(), j_access_token.obj());
286 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
287 JNIEnv* env,
288 jobject obj,
289 jstring j_current_acc,
290 jboolean j_force_notifications) {
291 std::string signed_in_account;
292 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts from java";
293 if (j_current_acc)
294 signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc);
295 if (!signed_in_account.empty())
296 signed_in_account = gaia::CanonicalizeEmail(signed_in_account);
298 // Clear any auth errors so that client can retry to get access tokens.
299 errors_.clear();
301 ValidateAccounts(MapAccountNameToAccountId(signed_in_account),
302 j_force_notifications != JNI_FALSE);
305 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
306 const std::string& signed_in_account,
307 bool force_notifications) {
308 std::vector<std::string> prev_ids = GetAccounts();
309 std::vector<std::string> curr_ids = GetSystemAccountNames();
310 std::vector<std::string> refreshed_ids;
311 std::vector<std::string> revoked_ids;
312 bool account_validation_result = true;
314 for (size_t i = 0; i < curr_ids.size(); ++i)
315 curr_ids[i] = MapAccountNameToAccountId(curr_ids[i]);
317 for (size_t i = 0; i < prev_ids.size(); ++i)
318 ValidateAccountId(prev_ids[i]);
320 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
321 << " sigined_in_account=" << signed_in_account
322 << " prev_ids=" << prev_ids.size() << " curr_ids=" << curr_ids.size()
323 << " force=" << (force_notifications ? "true" : "false");
325 account_validation_result =
326 ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids,
327 revoked_ids, force_notifications);
329 ScopedBatchChange batch(this);
331 JNIEnv* env = AttachCurrentThread();
332 ScopedJavaLocalRef<jobjectArray> java_accounts;
333 if (account_validation_result) {
334 java_accounts = base::android::ToJavaArrayOfStrings(env, curr_ids);
335 } else {
336 java_accounts =
337 base::android::ToJavaArrayOfStrings(env, std::vector<std::string>());
339 Java_OAuth2TokenService_saveStoredAccounts(
340 env, base::android::GetApplicationContext(), java_accounts.obj());
342 for (std::vector<std::string>::iterator it = refreshed_ids.begin();
343 it != refreshed_ids.end(); it++) {
344 FireRefreshTokenAvailable(*it);
347 for (std::vector<std::string>::iterator it = revoked_ids.begin();
348 it != revoked_ids.end(); it++) {
349 FireRefreshTokenRevoked(*it);
352 if (fire_refresh_token_loaded_ == RT_WAIT_FOR_VALIDATION) {
353 fire_refresh_token_loaded_ = RT_LOADED;
354 FireRefreshTokensLoaded();
355 } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) {
356 fire_refresh_token_loaded_ = RT_HAS_BEEN_VALIDATED;
359 // Clear accounts no longer exist on device from AccountTrackerService.
360 std::vector<AccountInfo> accounts_info =
361 account_tracker_service_->GetAccounts();
362 for (auto info : accounts_info) {
363 auto it = curr_ids.begin();
364 for (; it != curr_ids.end(); ++it) {
365 if (*it == info.account_id)
366 break;
368 if (it == curr_ids.end())
369 account_tracker_service_->RemoveAccount(info.account_id);
372 // No need to wait for SigninManager to finish migration if not signed in.
373 if (account_tracker_service_->GetMigrationState() ==
374 AccountTrackerService::MIGRATION_IN_PROGRESS &&
375 signed_in_account.empty()) {
376 account_tracker_service_->SetMigrationDone();
380 bool OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
381 const std::string& signed_in_account,
382 const std::vector<std::string>& prev_account_ids,
383 const std::vector<std::string>& curr_account_ids,
384 std::vector<std::string>& refreshed_ids,
385 std::vector<std::string>& revoked_ids,
386 bool force_notifications) {
387 if (std::find(curr_account_ids.begin(), curr_account_ids.end(),
388 signed_in_account) != curr_account_ids.end()) {
389 // Test to see if an account is removed from the Android AccountManager.
390 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
391 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
392 it != prev_account_ids.end(); it++) {
393 if (*it == signed_in_account)
394 continue;
396 if (std::find(curr_account_ids.begin(), curr_account_ids.end(), *it) ==
397 curr_account_ids.end()) {
398 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
399 << "revoked=" << *it;
400 revoked_ids.push_back(*it);
404 if (force_notifications ||
405 std::find(prev_account_ids.begin(), prev_account_ids.end(),
406 signed_in_account) == prev_account_ids.end()) {
407 // Always fire the primary signed in account first.
408 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
409 << "refreshed=" << signed_in_account;
410 refreshed_ids.push_back(signed_in_account);
413 for (std::vector<std::string>::const_iterator it = curr_account_ids.begin();
414 it != curr_account_ids.end(); it++) {
415 if (*it != signed_in_account) {
416 if (force_notifications ||
417 std::find(prev_account_ids.begin(), prev_account_ids.end(), *it) ==
418 prev_account_ids.end()) {
419 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
420 << "refreshed=" << *it;
421 refreshed_ids.push_back(*it);
425 return true;
426 } else {
427 // Currently signed in account does not any longer exist among accounts on
428 // system together with all other accounts.
429 if (std::find(prev_account_ids.begin(), prev_account_ids.end(),
430 signed_in_account) != prev_account_ids.end()) {
431 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
432 << "revoked=" << signed_in_account;
433 revoked_ids.push_back(signed_in_account);
435 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
436 it != prev_account_ids.end(); it++) {
437 if (*it == signed_in_account)
438 continue;
439 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
440 << "revoked=" << *it;
441 revoked_ids.push_back(*it);
443 return false;
447 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailableFromJava(
448 JNIEnv* env,
449 jobject obj,
450 const jstring account_name) {
451 std::string account_id =
452 MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name));
453 // Notify native observers.
454 FireRefreshTokenAvailable(account_id);
457 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable(
458 const std::string& account_id) {
459 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable id="
460 << account_id;
461 JNIEnv* env = AttachCurrentThread();
462 ScopedJavaLocalRef<jstring> account_name =
463 ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id));
464 Java_OAuth2TokenService_notifyRefreshTokenAvailable(env, java_ref_.obj(),
465 account_name.obj());
466 OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id);
469 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevokedFromJava(
470 JNIEnv* env,
471 jobject obj,
472 const jstring account_name) {
473 std::string account_id =
474 MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name));
475 // Notify native observers.
476 FireRefreshTokenRevoked(account_id);
479 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked(
480 const std::string& account_id) {
481 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id="
482 << account_id;
483 JNIEnv* env = AttachCurrentThread();
484 ScopedJavaLocalRef<jstring> account_name =
485 ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id));
486 Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_.obj(),
487 account_name.obj());
488 OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id);
491 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoadedFromJava(
492 JNIEnv* env,
493 jobject obj) {
494 // Notify native observers.
495 FireRefreshTokensLoaded();
498 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() {
499 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded";
500 JNIEnv* env = AttachCurrentThread();
501 Java_OAuth2TokenService_notifyRefreshTokensLoaded(env, java_ref_.obj());
502 OAuth2TokenServiceDelegate::FireRefreshTokensLoaded();
505 void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() {
506 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials";
507 ScopedBatchChange batch(this);
508 std::vector<std::string> accounts = GetAccounts();
509 for (std::vector<std::string>::iterator it = accounts.begin();
510 it != accounts.end(); it++) {
511 FireRefreshTokenRevoked(*it);
514 // Clear everything on the Java side as well.
515 std::vector<std::string> empty;
516 JNIEnv* env = AttachCurrentThread();
517 ScopedJavaLocalRef<jobjectArray> java_accounts(
518 base::android::ToJavaArrayOfStrings(env, empty));
519 Java_OAuth2TokenService_saveStoredAccounts(
520 env, base::android::GetApplicationContext(), java_accounts.obj());
523 void OAuth2TokenServiceDelegateAndroid::LoadCredentials(
524 const std::string& primary_account_id) {
525 if (fire_refresh_token_loaded_ == RT_HAS_BEEN_VALIDATED) {
526 fire_refresh_token_loaded_ = RT_LOADED;
527 FireRefreshTokensLoaded();
528 } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) {
529 fire_refresh_token_loaded_ = RT_WAIT_FOR_VALIDATION;
533 std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName(
534 const std::string& account_id) const {
535 std::string account_name =
536 account_tracker_service_->GetAccountInfo(account_id).email;
537 DCHECK(!account_name.empty() || account_id.empty());
538 return account_name;
541 std::string OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId(
542 const std::string& account_name) const {
543 std::string account_id =
544 account_tracker_service_->FindAccountInfoByEmail(account_name).account_id;
545 DCHECK(!account_id.empty() || account_name.empty());
546 return account_id;
549 // Called from Java when fetching of an OAuth2 token is finished. The
550 // |authToken| param is only valid when |result| is true.
551 void OAuth2TokenFetched(JNIEnv* env,
552 const JavaParamRef<jclass>& clazz,
553 const JavaParamRef<jstring>& authToken,
554 jboolean isTransientError,
555 jlong nativeCallback) {
556 std::string token;
557 if (authToken)
558 token = ConvertJavaStringToUTF8(env, authToken);
559 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
560 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
561 GoogleServiceAuthError
562 err(authToken
563 ? GoogleServiceAuthError::NONE
564 : isTransientError
565 ? GoogleServiceAuthError::CONNECTION_FAILED
566 : GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
567 heap_callback->Run(err, token, base::Time());
570 // static
571 bool OAuth2TokenServiceDelegateAndroid::Register(JNIEnv* env) {
572 return RegisterNativesImpl(env);