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 "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"
20 using base::android::AttachCurrentThread
;
21 using base::android::ConvertJavaStringToUTF8
;
22 using base::android::ConvertUTF8ToJavaString
;
23 using base::android::ScopedJavaLocalRef
;
24 using content::BrowserThread
;
28 // Callback from FetchOAuth2TokenWithUsername().
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
34 typedef base::Callback
<void(const GoogleServiceAuthError
&,
36 const base::Time
&)> FetchOAuth2TokenCallback
;
38 class AndroidAccessTokenFetcher
: public OAuth2AccessTokenFetcher
{
40 AndroidAccessTokenFetcher(OAuth2AccessTokenConsumer
* consumer
,
41 const std::string
& account_id
);
42 ~AndroidAccessTokenFetcher() override
;
44 // Overrides from OAuth2AccessTokenFetcher:
45 void Start(const std::string
& client_id
,
46 const std::string
& client_secret
,
47 const std::vector
<std::string
>& scopes
) override
;
48 void CancelRequest() override
;
50 // Handles an access token response.
51 void OnAccessTokenResponse(const GoogleServiceAuthError
& error
,
52 const std::string
& access_token
,
53 const base::Time
& expiration_time
);
56 std::string
CombineScopes(const std::vector
<std::string
>& scopes
);
58 std::string account_id_
;
59 bool request_was_cancelled_
;
60 base::WeakPtrFactory
<AndroidAccessTokenFetcher
> weak_factory_
;
62 DISALLOW_COPY_AND_ASSIGN(AndroidAccessTokenFetcher
);
65 AndroidAccessTokenFetcher::AndroidAccessTokenFetcher(
66 OAuth2AccessTokenConsumer
* consumer
,
67 const std::string
& account_id
)
68 : OAuth2AccessTokenFetcher(consumer
),
69 account_id_(account_id
),
70 request_was_cancelled_(false),
74 AndroidAccessTokenFetcher::~AndroidAccessTokenFetcher() {
77 void AndroidAccessTokenFetcher::Start(const std::string
& client_id
,
78 const std::string
& client_secret
,
79 const std::vector
<std::string
>& scopes
) {
80 JNIEnv
* env
= AttachCurrentThread();
81 std::string scope
= CombineScopes(scopes
);
82 ScopedJavaLocalRef
<jstring
> j_username
=
83 ConvertUTF8ToJavaString(env
, account_id_
);
84 ScopedJavaLocalRef
<jstring
> j_scope
= ConvertUTF8ToJavaString(env
, scope
);
85 scoped_ptr
<FetchOAuth2TokenCallback
> heap_callback(
86 new FetchOAuth2TokenCallback(
87 base::Bind(&AndroidAccessTokenFetcher::OnAccessTokenResponse
,
88 weak_factory_
.GetWeakPtr())));
90 // Call into Java to get a new token.
91 Java_OAuth2TokenService_getOAuth2AuthToken(
92 env
, base::android::GetApplicationContext(), j_username
.obj(),
93 j_scope
.obj(), reinterpret_cast<intptr_t>(heap_callback
.release()));
96 void AndroidAccessTokenFetcher::CancelRequest() {
97 request_was_cancelled_
= true;
100 void AndroidAccessTokenFetcher::OnAccessTokenResponse(
101 const GoogleServiceAuthError
& error
,
102 const std::string
& access_token
,
103 const base::Time
& expiration_time
) {
104 if (request_was_cancelled_
) {
105 // Ignore the callback if the request was cancelled.
108 if (error
.state() == GoogleServiceAuthError::NONE
) {
109 FireOnGetTokenSuccess(access_token
, expiration_time
);
111 FireOnGetTokenFailure(error
);
116 std::string
AndroidAccessTokenFetcher::CombineScopes(
117 const std::vector
<std::string
>& scopes
) {
118 // The Android AccountManager supports multiple scopes separated by a space:
119 // https://code.google.com/p/google-api-java-client/wiki/OAuth2#Android
121 for (std::vector
<std::string
>::const_iterator it
= scopes
.begin();
122 it
!= scopes
.end(); ++it
) {
132 bool OAuth2TokenServiceDelegateAndroid::is_testing_profile_
= false;
134 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo()
135 : error(GoogleServiceAuthError::NONE
) {}
137 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo(
138 const GoogleServiceAuthError
& error
)
141 OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid() {
142 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ctor";
143 JNIEnv
* env
= AttachCurrentThread();
144 base::android::ScopedJavaLocalRef
<jobject
> local_java_ref
=
145 Java_OAuth2TokenService_create(env
, reinterpret_cast<intptr_t>(this));
146 java_ref_
.Reset(env
, local_java_ref
.obj());
149 OAuth2TokenServiceDelegateAndroid::~OAuth2TokenServiceDelegateAndroid() {
153 ScopedJavaLocalRef
<jobject
> OAuth2TokenServiceDelegateAndroid::GetForProfile(
156 jobject j_profile_android
) {
157 Profile
* profile
= ProfileAndroid::FromProfileAndroid(j_profile_android
);
158 ProfileOAuth2TokenService
* service
=
159 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
);
160 OAuth2TokenServiceDelegate
* delegate
= service
->GetDelegate();
161 return ScopedJavaLocalRef
<jobject
>(
162 static_cast<OAuth2TokenServiceDelegateAndroid
*>(delegate
)->java_ref_
);
165 static ScopedJavaLocalRef
<jobject
> GetForProfile(JNIEnv
* env
,
167 jobject j_profile_android
) {
168 return OAuth2TokenServiceDelegateAndroid::GetForProfile(env
, clazz
,
172 void OAuth2TokenServiceDelegateAndroid::Initialize() {
173 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::Initialize";
174 if (!is_testing_profile_
) {
175 Java_OAuth2TokenService_validateAccounts(
176 AttachCurrentThread(), java_ref_
.obj(),
177 base::android::GetApplicationContext(), JNI_TRUE
);
181 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable(
182 const std::string
& account_id
) const {
183 JNIEnv
* env
= AttachCurrentThread();
184 ScopedJavaLocalRef
<jstring
> j_account_id
=
185 ConvertUTF8ToJavaString(env
, account_id
);
186 jboolean refresh_token_is_available
=
187 Java_OAuth2TokenService_hasOAuth2RefreshToken(
188 env
, base::android::GetApplicationContext(), j_account_id
.obj());
189 return refresh_token_is_available
== JNI_TRUE
;
192 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenHasError(
193 const std::string
& account_id
) const {
194 auto it
= errors_
.find(account_id
);
195 // TODO(rogerta): should we distinguish between transient and persistent?
196 return it
== errors_
.end() ? false : IsError(it
->second
.error
);
199 void OAuth2TokenServiceDelegateAndroid::UpdateAuthError(
200 const std::string
& account_id
,
201 const GoogleServiceAuthError
& error
) {
202 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError"
203 << " account=" << account_id
204 << " error=" << error
.ToString();
205 if (error
.state() == GoogleServiceAuthError::NONE
) {
206 errors_
.erase(account_id
);
208 // TODO(rogerta): should we distinguish between transient and persistent?
209 errors_
[account_id
] = ErrorInfo(error
);
213 std::vector
<std::string
> OAuth2TokenServiceDelegateAndroid::GetAccounts() {
214 std::vector
<std::string
> accounts
;
215 JNIEnv
* env
= AttachCurrentThread();
216 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
217 Java_OAuth2TokenService_getAccounts(
218 env
, base::android::GetApplicationContext());
219 // TODO(fgorski): We may decide to filter out some of the accounts.
220 base::android::AppendJavaStringArrayToStringVector(env
, j_accounts
.obj(),
225 std::vector
<std::string
>
226 OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() {
227 std::vector
<std::string
> accounts
;
228 JNIEnv
* env
= AttachCurrentThread();
229 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
230 Java_OAuth2TokenService_getSystemAccounts(
231 env
, base::android::GetApplicationContext());
232 base::android::AppendJavaStringArrayToStringVector(env
, j_accounts
.obj(),
237 OAuth2AccessTokenFetcher
*
238 OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher(
239 const std::string
& account_id
,
240 net::URLRequestContextGetter
* getter
,
241 OAuth2AccessTokenConsumer
* consumer
) {
242 ValidateAccountId(account_id
);
243 return new AndroidAccessTokenFetcher(consumer
, account_id
);
246 void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken(
247 const std::string
& account_id
,
248 const std::string
& client_id
,
249 const OAuth2TokenService::ScopeSet
& scopes
,
250 const std::string
& access_token
) {
251 ValidateAccountId(account_id
);
252 JNIEnv
* env
= AttachCurrentThread();
253 ScopedJavaLocalRef
<jstring
> j_access_token
=
254 ConvertUTF8ToJavaString(env
, access_token
);
255 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
256 env
, base::android::GetApplicationContext(), j_access_token
.obj());
259 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
262 jstring j_current_acc
,
263 jboolean j_force_notifications
) {
264 std::string signed_in_account
;
265 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts from java";
267 signed_in_account
= ConvertJavaStringToUTF8(env
, j_current_acc
);
268 if (!signed_in_account
.empty())
269 signed_in_account
= gaia::CanonicalizeEmail(signed_in_account
);
271 // Clear any auth errors so that client can retry to get access tokens.
274 ValidateAccounts(signed_in_account
, j_force_notifications
!= JNI_FALSE
);
277 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
278 const std::string
& signed_in_account
,
279 bool force_notifications
) {
280 std::vector
<std::string
> prev_ids
= GetAccounts();
281 std::vector
<std::string
> curr_ids
= GetSystemAccounts();
282 std::vector
<std::string
> refreshed_ids
;
283 std::vector
<std::string
> revoked_ids
;
285 // Canonicalize system accounts. |prev_ids| is already done.
286 for (size_t i
= 0; i
< curr_ids
.size(); ++i
)
287 curr_ids
[i
] = gaia::CanonicalizeEmail(curr_ids
[i
]);
288 for (size_t i
= 0; i
< prev_ids
.size(); ++i
)
289 ValidateAccountId(prev_ids
[i
]);
291 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
292 << " sigined_in_account=" << signed_in_account
293 << " prev_ids=" << prev_ids
.size() << " curr_ids=" << curr_ids
.size()
294 << " force=" << (force_notifications
? "true" : "false");
296 if (!ValidateAccounts(signed_in_account
, prev_ids
, curr_ids
, refreshed_ids
,
297 revoked_ids
, force_notifications
)) {
301 ScopedBatchChange
batch(this);
303 JNIEnv
* env
= AttachCurrentThread();
304 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
305 base::android::ToJavaArrayOfStrings(env
, curr_ids
));
306 Java_OAuth2TokenService_saveStoredAccounts(
307 env
, base::android::GetApplicationContext(), java_accounts
.obj());
309 for (std::vector
<std::string
>::iterator it
= refreshed_ids
.begin();
310 it
!= refreshed_ids
.end(); it
++) {
311 FireRefreshTokenAvailable(*it
);
314 for (std::vector
<std::string
>::iterator it
= revoked_ids
.begin();
315 it
!= revoked_ids
.end(); it
++) {
316 FireRefreshTokenRevoked(*it
);
320 bool OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
321 const std::string
& signed_in_account
,
322 const std::vector
<std::string
>& prev_account_ids
,
323 const std::vector
<std::string
>& curr_account_ids
,
324 std::vector
<std::string
>& refreshed_ids
,
325 std::vector
<std::string
>& revoked_ids
,
326 bool force_notifications
) {
327 if (std::find(curr_account_ids
.begin(), curr_account_ids
.end(),
328 signed_in_account
) != curr_account_ids
.end()) {
329 // Test to see if an account is removed from the Android AccountManager.
330 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
331 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
332 it
!= prev_account_ids
.end(); it
++) {
333 if (*it
== signed_in_account
)
336 if (std::find(curr_account_ids
.begin(), curr_account_ids
.end(), *it
) ==
337 curr_account_ids
.end()) {
338 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
339 << "revoked=" << *it
;
340 revoked_ids
.push_back(*it
);
344 if (force_notifications
||
345 std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
346 signed_in_account
) == prev_account_ids
.end()) {
347 // Always fire the primary signed in account first.
348 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
349 << "refreshed=" << signed_in_account
;
350 refreshed_ids
.push_back(signed_in_account
);
353 for (std::vector
<std::string
>::const_iterator it
= curr_account_ids
.begin();
354 it
!= curr_account_ids
.end(); it
++) {
355 if (*it
!= signed_in_account
) {
356 if (force_notifications
||
357 std::find(prev_account_ids
.begin(), prev_account_ids
.end(), *it
) ==
358 prev_account_ids
.end()) {
359 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
360 << "refreshed=" << *it
;
361 refreshed_ids
.push_back(*it
);
367 // Currently signed in account does not any longer exist among accounts on
368 // system together with all other accounts.
369 if (std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
370 signed_in_account
) != prev_account_ids
.end()) {
371 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
372 << "revoked=" << signed_in_account
;
373 revoked_ids
.push_back(signed_in_account
);
375 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
376 it
!= prev_account_ids
.end(); it
++) {
377 if (*it
== signed_in_account
)
379 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
380 << "revoked=" << *it
;
381 revoked_ids
.push_back(*it
);
387 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailableFromJava(
390 const jstring account_name
) {
391 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
392 // Notify native observers.
393 FireRefreshTokenAvailable(account_id
);
396 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable(
397 const std::string
& account_id
) {
398 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable id="
400 JNIEnv
* env
= AttachCurrentThread();
401 ScopedJavaLocalRef
<jstring
> account_name
=
402 ConvertUTF8ToJavaString(env
, account_id
);
403 Java_OAuth2TokenService_notifyRefreshTokenAvailable(env
, java_ref_
.obj(),
405 OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id
);
408 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevokedFromJava(
411 const jstring account_name
) {
412 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
413 // Notify native observers.
414 FireRefreshTokenRevoked(account_id
);
417 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked(
418 const std::string
& account_id
) {
419 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id="
421 JNIEnv
* env
= AttachCurrentThread();
422 ScopedJavaLocalRef
<jstring
> account_name
=
423 ConvertUTF8ToJavaString(env
, account_id
);
424 Java_OAuth2TokenService_notifyRefreshTokenRevoked(env
, java_ref_
.obj(),
426 OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id
);
429 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoadedFromJava(
432 // Notify native observers.
433 FireRefreshTokensLoaded();
436 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() {
437 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded";
438 JNIEnv
* env
= AttachCurrentThread();
439 Java_OAuth2TokenService_notifyRefreshTokensLoaded(env
, java_ref_
.obj());
440 OAuth2TokenServiceDelegate::FireRefreshTokensLoaded();
443 void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() {
444 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials";
445 ScopedBatchChange
batch(this);
446 std::vector
<std::string
> accounts
= GetAccounts();
447 for (std::vector
<std::string
>::iterator it
= accounts
.begin();
448 it
!= accounts
.end(); it
++) {
449 FireRefreshTokenRevoked(*it
);
452 // Clear everything on the Java side as well.
453 std::vector
<std::string
> empty
;
454 JNIEnv
* env
= AttachCurrentThread();
455 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
456 base::android::ToJavaArrayOfStrings(env
, empty
));
457 Java_OAuth2TokenService_saveStoredAccounts(
458 env
, base::android::GetApplicationContext(), java_accounts
.obj());
461 // Called from Java when fetching of an OAuth2 token is finished. The
462 // |authToken| param is only valid when |result| is true.
463 void OAuth2TokenFetched(JNIEnv
* env
,
466 jboolean isTransientError
,
467 jlong nativeCallback
) {
470 token
= ConvertJavaStringToUTF8(env
, authToken
);
471 scoped_ptr
<FetchOAuth2TokenCallback
> heap_callback(
472 reinterpret_cast<FetchOAuth2TokenCallback
*>(nativeCallback
));
473 GoogleServiceAuthError
475 ? GoogleServiceAuthError::NONE
477 ? GoogleServiceAuthError::CONNECTION_FAILED
478 : GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
);
479 heap_callback
->Run(err
, token
, base::Time());
483 bool OAuth2TokenServiceDelegateAndroid::Register(JNIEnv
* env
) {
484 return RegisterNativesImpl(env
);