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(
167 const JavaParamRef
<jclass
>& clazz
,
168 const JavaParamRef
<jobject
>& j_profile_android
) {
169 return OAuth2TokenServiceDelegateAndroid::GetForProfile(env
, clazz
,
173 void OAuth2TokenServiceDelegateAndroid::Initialize() {
174 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::Initialize";
175 if (!is_testing_profile_
) {
176 Java_OAuth2TokenService_validateAccounts(
177 AttachCurrentThread(), java_ref_
.obj(),
178 base::android::GetApplicationContext(), JNI_TRUE
);
182 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable(
183 const std::string
& account_id
) const {
184 JNIEnv
* env
= AttachCurrentThread();
185 ScopedJavaLocalRef
<jstring
> j_account_id
=
186 ConvertUTF8ToJavaString(env
, account_id
);
187 jboolean refresh_token_is_available
=
188 Java_OAuth2TokenService_hasOAuth2RefreshToken(
189 env
, base::android::GetApplicationContext(), j_account_id
.obj());
190 return refresh_token_is_available
== JNI_TRUE
;
193 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenHasError(
194 const std::string
& account_id
) const {
195 auto it
= errors_
.find(account_id
);
196 // TODO(rogerta): should we distinguish between transient and persistent?
197 return it
== errors_
.end() ? false : IsError(it
->second
.error
);
200 void OAuth2TokenServiceDelegateAndroid::UpdateAuthError(
201 const std::string
& account_id
,
202 const GoogleServiceAuthError
& error
) {
203 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError"
204 << " account=" << account_id
205 << " error=" << error
.ToString();
206 if (error
.state() == GoogleServiceAuthError::NONE
) {
207 errors_
.erase(account_id
);
209 // TODO(rogerta): should we distinguish between transient and persistent?
210 errors_
[account_id
] = ErrorInfo(error
);
214 std::vector
<std::string
> OAuth2TokenServiceDelegateAndroid::GetAccounts() {
215 std::vector
<std::string
> accounts
;
216 JNIEnv
* env
= AttachCurrentThread();
217 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
218 Java_OAuth2TokenService_getAccounts(
219 env
, base::android::GetApplicationContext());
220 // TODO(fgorski): We may decide to filter out some of the accounts.
221 base::android::AppendJavaStringArrayToStringVector(env
, j_accounts
.obj(),
226 std::vector
<std::string
>
227 OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() {
228 std::vector
<std::string
> accounts
;
229 JNIEnv
* env
= AttachCurrentThread();
230 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
231 Java_OAuth2TokenService_getSystemAccounts(
232 env
, base::android::GetApplicationContext());
233 base::android::AppendJavaStringArrayToStringVector(env
, j_accounts
.obj(),
238 OAuth2AccessTokenFetcher
*
239 OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher(
240 const std::string
& account_id
,
241 net::URLRequestContextGetter
* getter
,
242 OAuth2AccessTokenConsumer
* consumer
) {
243 ValidateAccountId(account_id
);
244 return new AndroidAccessTokenFetcher(consumer
, account_id
);
247 void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken(
248 const std::string
& account_id
,
249 const std::string
& client_id
,
250 const OAuth2TokenService::ScopeSet
& scopes
,
251 const std::string
& access_token
) {
252 ValidateAccountId(account_id
);
253 JNIEnv
* env
= AttachCurrentThread();
254 ScopedJavaLocalRef
<jstring
> j_access_token
=
255 ConvertUTF8ToJavaString(env
, access_token
);
256 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
257 env
, base::android::GetApplicationContext(), j_access_token
.obj());
260 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
263 jstring j_current_acc
,
264 jboolean j_force_notifications
) {
265 std::string signed_in_account
;
266 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts from java";
268 signed_in_account
= ConvertJavaStringToUTF8(env
, j_current_acc
);
269 if (!signed_in_account
.empty())
270 signed_in_account
= gaia::CanonicalizeEmail(signed_in_account
);
272 // Clear any auth errors so that client can retry to get access tokens.
275 ValidateAccounts(signed_in_account
, j_force_notifications
!= JNI_FALSE
);
278 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
279 const std::string
& signed_in_account
,
280 bool force_notifications
) {
281 std::vector
<std::string
> prev_ids
= GetAccounts();
282 std::vector
<std::string
> curr_ids
= GetSystemAccounts();
283 std::vector
<std::string
> refreshed_ids
;
284 std::vector
<std::string
> revoked_ids
;
286 // Canonicalize system accounts. |prev_ids| is already done.
287 for (size_t i
= 0; i
< curr_ids
.size(); ++i
)
288 curr_ids
[i
] = gaia::CanonicalizeEmail(curr_ids
[i
]);
289 for (size_t i
= 0; i
< prev_ids
.size(); ++i
)
290 ValidateAccountId(prev_ids
[i
]);
292 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
293 << " sigined_in_account=" << signed_in_account
294 << " prev_ids=" << prev_ids
.size() << " curr_ids=" << curr_ids
.size()
295 << " force=" << (force_notifications
? "true" : "false");
297 if (!ValidateAccounts(signed_in_account
, prev_ids
, curr_ids
, refreshed_ids
,
298 revoked_ids
, force_notifications
)) {
302 ScopedBatchChange
batch(this);
304 JNIEnv
* env
= AttachCurrentThread();
305 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
306 base::android::ToJavaArrayOfStrings(env
, curr_ids
));
307 Java_OAuth2TokenService_saveStoredAccounts(
308 env
, base::android::GetApplicationContext(), java_accounts
.obj());
310 for (std::vector
<std::string
>::iterator it
= refreshed_ids
.begin();
311 it
!= refreshed_ids
.end(); it
++) {
312 FireRefreshTokenAvailable(*it
);
315 for (std::vector
<std::string
>::iterator it
= revoked_ids
.begin();
316 it
!= revoked_ids
.end(); it
++) {
317 FireRefreshTokenRevoked(*it
);
321 bool OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
322 const std::string
& signed_in_account
,
323 const std::vector
<std::string
>& prev_account_ids
,
324 const std::vector
<std::string
>& curr_account_ids
,
325 std::vector
<std::string
>& refreshed_ids
,
326 std::vector
<std::string
>& revoked_ids
,
327 bool force_notifications
) {
328 if (std::find(curr_account_ids
.begin(), curr_account_ids
.end(),
329 signed_in_account
) != curr_account_ids
.end()) {
330 // Test to see if an account is removed from the Android AccountManager.
331 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
332 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
333 it
!= prev_account_ids
.end(); it
++) {
334 if (*it
== signed_in_account
)
337 if (std::find(curr_account_ids
.begin(), curr_account_ids
.end(), *it
) ==
338 curr_account_ids
.end()) {
339 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
340 << "revoked=" << *it
;
341 revoked_ids
.push_back(*it
);
345 if (force_notifications
||
346 std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
347 signed_in_account
) == prev_account_ids
.end()) {
348 // Always fire the primary signed in account first.
349 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
350 << "refreshed=" << signed_in_account
;
351 refreshed_ids
.push_back(signed_in_account
);
354 for (std::vector
<std::string
>::const_iterator it
= curr_account_ids
.begin();
355 it
!= curr_account_ids
.end(); it
++) {
356 if (*it
!= signed_in_account
) {
357 if (force_notifications
||
358 std::find(prev_account_ids
.begin(), prev_account_ids
.end(), *it
) ==
359 prev_account_ids
.end()) {
360 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
361 << "refreshed=" << *it
;
362 refreshed_ids
.push_back(*it
);
368 // Currently signed in account does not any longer exist among accounts on
369 // system together with all other accounts.
370 if (std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
371 signed_in_account
) != prev_account_ids
.end()) {
372 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
373 << "revoked=" << signed_in_account
;
374 revoked_ids
.push_back(signed_in_account
);
376 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
377 it
!= prev_account_ids
.end(); it
++) {
378 if (*it
== signed_in_account
)
380 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
381 << "revoked=" << *it
;
382 revoked_ids
.push_back(*it
);
388 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailableFromJava(
391 const jstring account_name
) {
392 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
393 // Notify native observers.
394 FireRefreshTokenAvailable(account_id
);
397 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable(
398 const std::string
& account_id
) {
399 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable id="
401 JNIEnv
* env
= AttachCurrentThread();
402 ScopedJavaLocalRef
<jstring
> account_name
=
403 ConvertUTF8ToJavaString(env
, account_id
);
404 Java_OAuth2TokenService_notifyRefreshTokenAvailable(env
, java_ref_
.obj(),
406 OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id
);
409 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevokedFromJava(
412 const jstring account_name
) {
413 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
414 // Notify native observers.
415 FireRefreshTokenRevoked(account_id
);
418 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked(
419 const std::string
& account_id
) {
420 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id="
422 JNIEnv
* env
= AttachCurrentThread();
423 ScopedJavaLocalRef
<jstring
> account_name
=
424 ConvertUTF8ToJavaString(env
, account_id
);
425 Java_OAuth2TokenService_notifyRefreshTokenRevoked(env
, java_ref_
.obj(),
427 OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id
);
430 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoadedFromJava(
433 // Notify native observers.
434 FireRefreshTokensLoaded();
437 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() {
438 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded";
439 JNIEnv
* env
= AttachCurrentThread();
440 Java_OAuth2TokenService_notifyRefreshTokensLoaded(env
, java_ref_
.obj());
441 OAuth2TokenServiceDelegate::FireRefreshTokensLoaded();
444 void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() {
445 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials";
446 ScopedBatchChange
batch(this);
447 std::vector
<std::string
> accounts
= GetAccounts();
448 for (std::vector
<std::string
>::iterator it
= accounts
.begin();
449 it
!= accounts
.end(); it
++) {
450 FireRefreshTokenRevoked(*it
);
453 // Clear everything on the Java side as well.
454 std::vector
<std::string
> empty
;
455 JNIEnv
* env
= AttachCurrentThread();
456 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
457 base::android::ToJavaArrayOfStrings(env
, empty
));
458 Java_OAuth2TokenService_saveStoredAccounts(
459 env
, base::android::GetApplicationContext(), java_accounts
.obj());
462 // Called from Java when fetching of an OAuth2 token is finished. The
463 // |authToken| param is only valid when |result| is true.
464 void OAuth2TokenFetched(JNIEnv
* env
,
465 const JavaParamRef
<jclass
>& clazz
,
466 const JavaParamRef
<jstring
>& authToken
,
467 jboolean isTransientError
,
468 jlong nativeCallback
) {
471 token
= ConvertJavaStringToUTF8(env
, authToken
);
472 scoped_ptr
<FetchOAuth2TokenCallback
> heap_callback(
473 reinterpret_cast<FetchOAuth2TokenCallback
*>(nativeCallback
));
474 GoogleServiceAuthError
476 ? GoogleServiceAuthError::NONE
478 ? GoogleServiceAuthError::CONNECTION_FAILED
479 : GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
);
480 heap_callback
->Run(err
, token
, base::Time());
484 bool OAuth2TokenServiceDelegateAndroid::Register(JNIEnv
* env
) {
485 return RegisterNativesImpl(env
);