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/android_profile_oauth2_token_service.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(
35 const GoogleServiceAuthError
&, const std::string
&, const base::Time
&)>
36 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() {}
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())));
90 // Call into Java to get a new token.
91 Java_OAuth2TokenService_getOAuth2AuthToken(
92 env
, base::android::GetApplicationContext(),
95 reinterpret_cast<intptr_t>(heap_callback
.release()));
98 void AndroidAccessTokenFetcher::CancelRequest() {
99 request_was_cancelled_
= true;
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.
110 if (error
.state() == GoogleServiceAuthError::NONE
) {
111 FireOnGetTokenSuccess(access_token
, expiration_time
);
113 FireOnGetTokenFailure(error
);
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
123 for (std::vector
<std::string
>::const_iterator it
= scopes
.begin();
124 it
!= scopes
.end(); ++it
) {
134 bool AndroidProfileOAuth2TokenService::is_testing_profile_
= false;
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());
144 AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
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();
155 static jobject
GetForProfile(JNIEnv
* env
,
157 jobject j_profile_android
) {
158 return AndroidProfileOAuth2TokenService::GetForProfile(
159 env
, clazz
, j_profile_android
);
162 void AndroidProfileOAuth2TokenService::Initialize(
163 SigninClient
* client
,
164 SigninErrorController
* signin_error_controller
) {
165 DVLOG(1) << "AndroidProfileOAuth2TokenService::Initialize";
166 ProfileOAuth2TokenService::Initialize(client
, signin_error_controller
);
168 if (!is_testing_profile_
) {
169 Java_OAuth2TokenService_validateAccounts(
170 AttachCurrentThread(), java_ref_
.obj(),
171 base::android::GetApplicationContext(), JNI_TRUE
);
175 bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable(
176 const std::string
& account_id
) const {
177 JNIEnv
* env
= AttachCurrentThread();
178 ScopedJavaLocalRef
<jstring
> j_account_id
=
179 ConvertUTF8ToJavaString(env
, account_id
);
180 jboolean refresh_token_is_available
=
181 Java_OAuth2TokenService_hasOAuth2RefreshToken(
182 env
, base::android::GetApplicationContext(),
184 return refresh_token_is_available
== JNI_TRUE
;
187 void AndroidProfileOAuth2TokenService::UpdateAuthError(
188 const std::string
& account_id
,
189 const GoogleServiceAuthError
& error
) {
190 // TODO(rogerta): do we need to update anything, or does the system handle it?
193 std::vector
<std::string
> AndroidProfileOAuth2TokenService::GetAccounts() {
194 std::vector
<std::string
> accounts
;
195 JNIEnv
* env
= AttachCurrentThread();
196 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
197 Java_OAuth2TokenService_getAccounts(
198 env
, base::android::GetApplicationContext());
199 // TODO(fgorski): We may decide to filter out some of the accounts.
200 base::android::AppendJavaStringArrayToStringVector(env
,
206 std::vector
<std::string
> AndroidProfileOAuth2TokenService::GetSystemAccounts() {
207 std::vector
<std::string
> accounts
;
208 JNIEnv
* env
= AttachCurrentThread();
209 ScopedJavaLocalRef
<jobjectArray
> j_accounts
=
210 Java_OAuth2TokenService_getSystemAccounts(
211 env
, base::android::GetApplicationContext());
212 base::android::AppendJavaStringArrayToStringVector(env
,
218 OAuth2AccessTokenFetcher
*
219 AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher(
220 const std::string
& account_id
,
221 net::URLRequestContextGetter
* getter
,
222 OAuth2AccessTokenConsumer
* consumer
) {
223 ValidateAccountId(account_id
);
224 return new AndroidAccessTokenFetcher(consumer
, account_id
);
227 void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token(
228 const std::string
& account_id
,
229 const std::string
& client_id
,
230 const ScopeSet
& scopes
,
231 const std::string
& access_token
) {
232 ValidateAccountId(account_id
);
233 OAuth2TokenService::InvalidateOAuth2Token(account_id
,
238 JNIEnv
* env
= AttachCurrentThread();
239 ScopedJavaLocalRef
<jstring
> j_access_token
=
240 ConvertUTF8ToJavaString(env
, access_token
);
241 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
242 env
, base::android::GetApplicationContext(),
243 j_access_token
.obj());
246 void AndroidProfileOAuth2TokenService::ValidateAccounts(
249 jstring j_current_acc
,
250 jboolean j_force_notifications
) {
251 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts from java";
252 std::string signed_in_account
;
254 signed_in_account
= ConvertJavaStringToUTF8(env
, j_current_acc
);
255 if (!signed_in_account
.empty())
256 signed_in_account
= gaia::CanonicalizeEmail(signed_in_account
);
257 ValidateAccounts(signed_in_account
, j_force_notifications
!= JNI_FALSE
);
260 void AndroidProfileOAuth2TokenService::ValidateAccounts(
261 const std::string
& signed_in_account
,
262 bool force_notifications
) {
263 std::vector
<std::string
> prev_ids
= GetAccounts();
264 std::vector
<std::string
> curr_ids
= GetSystemAccounts();
265 std::vector
<std::string
> refreshed_ids
;
266 std::vector
<std::string
> revoked_ids
;
268 // Canonicalize system accounts. |prev_ids| is already done.
269 for (size_t i
= 0; i
< curr_ids
.size(); ++i
)
270 curr_ids
[i
] = gaia::CanonicalizeEmail(curr_ids
[i
]);
271 for (size_t i
= 0; i
< prev_ids
.size(); ++i
)
272 ValidateAccountId(prev_ids
[i
]);
274 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
275 << " sigined_in_account=" << signed_in_account
276 << " prev_ids=" << prev_ids
.size()
277 << " curr_ids=" << curr_ids
.size()
278 << " force=" << (force_notifications
? "true" : "false");
280 if (!ValidateAccounts(signed_in_account
, prev_ids
, curr_ids
, refreshed_ids
,
281 revoked_ids
, force_notifications
)) {
285 ScopedBatchChange
batch(this);
287 JNIEnv
* env
= AttachCurrentThread();
288 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
289 base::android::ToJavaArrayOfStrings(env
, curr_ids
));
290 Java_OAuth2TokenService_saveStoredAccounts(
291 env
, base::android::GetApplicationContext(), java_accounts
.obj());
293 for (std::vector
<std::string
>::iterator it
= refreshed_ids
.begin();
294 it
!= refreshed_ids
.end(); it
++) {
295 FireRefreshTokenAvailable(*it
);
298 for (std::vector
<std::string
>::iterator it
= revoked_ids
.begin();
299 it
!= revoked_ids
.end(); it
++) {
300 FireRefreshTokenRevoked(*it
);
304 bool AndroidProfileOAuth2TokenService::ValidateAccounts(
305 const std::string
& signed_in_account
,
306 const std::vector
<std::string
>& prev_account_ids
,
307 const std::vector
<std::string
>& curr_account_ids
,
308 std::vector
<std::string
>& refreshed_ids
,
309 std::vector
<std::string
>& revoked_ids
,
310 bool force_notifications
) {
311 if (std::find(curr_account_ids
.begin(),
312 curr_account_ids
.end(),
313 signed_in_account
) != curr_account_ids
.end()) {
314 // Test to see if an account is removed from the Android AccountManager.
315 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
316 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
317 it
!= prev_account_ids
.end(); it
++) {
318 if (*it
== signed_in_account
)
321 if (std::find(curr_account_ids
.begin(),
322 curr_account_ids
.end(),
323 *it
) == curr_account_ids
.end()) {
324 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
325 << "revoked=" << *it
;
326 revoked_ids
.push_back(*it
);
330 if (force_notifications
||
331 std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
332 signed_in_account
) == prev_account_ids
.end()) {
333 // Always fire the primary signed in account first.
334 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
335 << "refreshed=" << signed_in_account
;
336 refreshed_ids
.push_back(signed_in_account
);
339 for (std::vector
<std::string
>::const_iterator it
= curr_account_ids
.begin();
340 it
!= curr_account_ids
.end(); it
++) {
341 if (*it
!= signed_in_account
) {
342 if (force_notifications
||
343 std::find(prev_account_ids
.begin(),
344 prev_account_ids
.end(),
345 *it
) == prev_account_ids
.end()) {
346 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
347 << "refreshed=" << *it
;
348 refreshed_ids
.push_back(*it
);
354 // Currently signed in account does not any longer exist among accounts on
355 // system together with all other accounts.
356 if (std::find(prev_account_ids
.begin(), prev_account_ids
.end(),
357 signed_in_account
) != prev_account_ids
.end()) {
358 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
359 << "revoked=" << signed_in_account
;
360 revoked_ids
.push_back(signed_in_account
);
362 for (std::vector
<std::string
>::const_iterator it
= prev_account_ids
.begin();
363 it
!= prev_account_ids
.end(); it
++) {
364 if (*it
== signed_in_account
)
366 DVLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
367 << "revoked=" << *it
;
368 revoked_ids
.push_back(*it
);
374 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
377 const jstring account_name
) {
378 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
379 AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id
);
382 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
383 const std::string
& account_id
) {
384 DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable id="
387 // Notify native observers.
388 OAuth2TokenService::FireRefreshTokenAvailable(account_id
);
389 // Notify Java observers.
390 JNIEnv
* env
= AttachCurrentThread();
391 ScopedJavaLocalRef
<jstring
> account_name
=
392 ConvertUTF8ToJavaString(env
, account_id
);
393 Java_OAuth2TokenService_notifyRefreshTokenAvailable(
394 env
, java_ref_
.obj(), account_name
.obj());
397 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
400 const jstring account_name
) {
401 std::string account_id
= ConvertJavaStringToUTF8(env
, account_name
);
402 AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id
);
405 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
406 const std::string
& account_id
) {
407 DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked id="
410 // Notify native observers.
411 OAuth2TokenService::FireRefreshTokenRevoked(account_id
);
412 // Notify Java observers.
413 JNIEnv
* env
= AttachCurrentThread();
414 ScopedJavaLocalRef
<jstring
> account_name
=
415 ConvertUTF8ToJavaString(env
, account_id
);
416 Java_OAuth2TokenService_notifyRefreshTokenRevoked(
417 env
, java_ref_
.obj(), account_name
.obj());
420 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
423 AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
426 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() {
427 DVLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded";
428 // Notify native observers.
429 OAuth2TokenService::FireRefreshTokensLoaded();
430 // Notify Java observers.
431 JNIEnv
* env
= AttachCurrentThread();
432 Java_OAuth2TokenService_notifyRefreshTokensLoaded(
433 env
, java_ref_
.obj());
436 void AndroidProfileOAuth2TokenService::RevokeAllCredentials() {
437 DVLOG(1) << "AndroidProfileOAuth2TokenService::RevokeAllCredentials";
438 ScopedBatchChange
batch(this);
439 std::vector
<std::string
> accounts
= GetAccounts();
440 for (std::vector
<std::string
>::iterator it
= accounts
.begin();
441 it
!= accounts
.end(); it
++) {
442 FireRefreshTokenRevoked(*it
);
445 // Clear everything on the Java side as well.
446 std::vector
<std::string
> empty
;
447 JNIEnv
* env
= AttachCurrentThread();
448 ScopedJavaLocalRef
<jobjectArray
> java_accounts(
449 base::android::ToJavaArrayOfStrings(env
, empty
));
450 Java_OAuth2TokenService_saveStoredAccounts(
451 env
, base::android::GetApplicationContext(), java_accounts
.obj());
454 // Called from Java when fetching of an OAuth2 token is finished. The
455 // |authToken| param is only valid when |result| is true.
456 void OAuth2TokenFetched(
461 jlong nativeCallback
) {
464 token
= ConvertJavaStringToUTF8(env
, authToken
);
465 scoped_ptr
<FetchOAuth2TokenCallback
> heap_callback(
466 reinterpret_cast<FetchOAuth2TokenCallback
*>(nativeCallback
));
467 // Android does not provide enough information to know if the credentials are
468 // wrong, so assume any error is transient by using CONNECTION_FAILED.
469 GoogleServiceAuthError
err(result
?
470 GoogleServiceAuthError::NONE
:
471 GoogleServiceAuthError::CONNECTION_FAILED
);
472 heap_callback
->Run(err
, token
, base::Time());
476 bool AndroidProfileOAuth2TokenService::Register(JNIEnv
* env
) {
477 return RegisterNativesImpl(env
);