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 "jni/OAuth2TokenService_jni.h"
18 using base::android::AttachCurrentThread;
19 using base::android::ConvertJavaStringToUTF8;
20 using base::android::ConvertUTF8ToJavaString;
21 using base::android::ScopedJavaLocalRef;
22 using content::BrowserThread;
26 std::string CombineScopes(const OAuth2TokenService::ScopeSet& scopes) {
27 // The Android AccountManager supports multiple scopes separated by a space:
28 // https://code.google.com/p/google-api-java-client/wiki/OAuth2#Android
30 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
31 it != scopes.end(); ++it) {
39 // Callback from FetchOAuth2TokenWithUsername().
41 // - the error, or NONE if the token fetch was successful.
42 // - the OAuth2 access token.
43 // - the expiry time of the token (may be null, indicating that the expiry
45 typedef base::Callback<void(
46 const GoogleServiceAuthError&, const std::string&, const base::Time&)>
47 FetchOAuth2TokenCallback;
51 bool AndroidProfileOAuth2TokenService::is_testing_profile_ = false;
53 AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService() {
54 VLOG(1) << "AndroidProfileOAuth2TokenService::ctor";
55 JNIEnv* env = AttachCurrentThread();
56 base::android::ScopedJavaLocalRef<jobject> local_java_ref =
57 Java_OAuth2TokenService_create(env, reinterpret_cast<intptr_t>(this));
58 java_ref_.Reset(env, local_java_ref.obj());
61 AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
64 jobject AndroidProfileOAuth2TokenService::GetForProfile(
65 JNIEnv* env, jclass clazz, jobject j_profile_android) {
66 Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
67 AndroidProfileOAuth2TokenService* service =
68 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(profile);
69 return service->java_ref_.obj();
72 static jobject GetForProfile(JNIEnv* env,
74 jobject j_profile_android) {
75 return AndroidProfileOAuth2TokenService::GetForProfile(
76 env, clazz, j_profile_android);
79 void AndroidProfileOAuth2TokenService::Initialize(SigninClient* client) {
80 VLOG(1) << "AndroidProfileOAuth2TokenService::Initialize";
81 ProfileOAuth2TokenService::Initialize(client);
83 if (!is_testing_profile_) {
84 Java_OAuth2TokenService_validateAccounts(
85 AttachCurrentThread(), java_ref_.obj(),
86 base::android::GetApplicationContext(), JNI_TRUE);
90 bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable(
91 const std::string& account_id) const {
92 JNIEnv* env = AttachCurrentThread();
93 ScopedJavaLocalRef<jstring> j_account_id =
94 ConvertUTF8ToJavaString(env, account_id);
95 jboolean refresh_token_is_available =
96 Java_OAuth2TokenService_hasOAuth2RefreshToken(
97 env, base::android::GetApplicationContext(),
99 return refresh_token_is_available == JNI_TRUE;
102 std::vector<std::string> AndroidProfileOAuth2TokenService::GetAccounts() {
103 std::vector<std::string> accounts;
104 JNIEnv* env = AttachCurrentThread();
105 ScopedJavaLocalRef<jobjectArray> j_accounts =
106 Java_OAuth2TokenService_getAccounts(
107 env, base::android::GetApplicationContext());
108 // TODO(fgorski): We may decide to filter out some of the accounts.
109 base::android::AppendJavaStringArrayToStringVector(env,
115 std::vector<std::string> AndroidProfileOAuth2TokenService::GetSystemAccounts() {
116 std::vector<std::string> accounts;
117 JNIEnv* env = AttachCurrentThread();
118 ScopedJavaLocalRef<jobjectArray> j_accounts =
119 Java_OAuth2TokenService_getSystemAccounts(
120 env, base::android::GetApplicationContext());
121 base::android::AppendJavaStringArrayToStringVector(env,
127 void AndroidProfileOAuth2TokenService::FetchOAuth2Token(
128 RequestImpl* request,
129 const std::string& account_id,
130 net::URLRequestContextGetter* getter,
131 const std::string& client_id,
132 const std::string& client_secret,
133 const OAuth2TokenService::ScopeSet& scopes) {
134 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
135 DCHECK(!account_id.empty());
137 JNIEnv* env = AttachCurrentThread();
138 std::string scope = CombineScopes(scopes);
139 ScopedJavaLocalRef<jstring> j_username =
140 ConvertUTF8ToJavaString(env, account_id);
141 ScopedJavaLocalRef<jstring> j_scope =
142 ConvertUTF8ToJavaString(env, scope);
144 // Allocate a copy of the request WeakPtr on the heap, because the object
145 // needs to be passed through JNI as an int.
146 // It will be passed back to OAuth2TokenFetched(), where it will be freed.
147 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
148 new FetchOAuth2TokenCallback(base::Bind(&RequestImpl::InformConsumer,
149 request->AsWeakPtr())));
151 // Call into Java to get a new token.
152 Java_OAuth2TokenService_getOAuth2AuthToken(
153 env, base::android::GetApplicationContext(),
156 reinterpret_cast<intptr_t>(heap_callback.release()));
159 OAuth2AccessTokenFetcher*
160 AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher(
161 const std::string& account_id,
162 net::URLRequestContextGetter* getter,
163 OAuth2AccessTokenConsumer* consumer) {
168 void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token(
169 const std::string& account_id,
170 const std::string& client_id,
171 const ScopeSet& scopes,
172 const std::string& access_token) {
173 OAuth2TokenService::InvalidateOAuth2Token(account_id,
178 JNIEnv* env = AttachCurrentThread();
179 ScopedJavaLocalRef<jstring> j_access_token =
180 ConvertUTF8ToJavaString(env, access_token);
181 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
182 env, base::android::GetApplicationContext(),
183 j_access_token.obj());
186 void AndroidProfileOAuth2TokenService::ValidateAccounts(
189 jstring j_current_acc,
190 jboolean j_force_notifications) {
191 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts from java";
192 std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc);
193 ValidateAccounts(signed_in_account, j_force_notifications != JNI_FALSE);
196 void AndroidProfileOAuth2TokenService::ValidateAccounts(
197 const std::string& signed_in_account,
198 bool force_notifications) {
199 std::vector<std::string> prev_ids = GetAccounts();
200 std::vector<std::string> curr_ids = GetSystemAccounts();
201 std::vector<std::string> refreshed_ids;
202 std::vector<std::string> revoked_ids;
204 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
205 << " sigined_in_account=" << signed_in_account
206 << " prev_ids=" << prev_ids.size()
207 << " curr_ids=" << curr_ids.size()
208 << " force=" << (force_notifications ? "true" : "false");
210 if (!ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids,
211 revoked_ids, force_notifications)) {
215 JNIEnv* env = AttachCurrentThread();
216 ScopedJavaLocalRef<jobjectArray> java_accounts(
217 base::android::ToJavaArrayOfStrings(env, curr_ids));
218 Java_OAuth2TokenService_saveStoredAccounts(
219 env, base::android::GetApplicationContext(), java_accounts.obj());
221 for (std::vector<std::string>::iterator it = refreshed_ids.begin();
222 it != refreshed_ids.end(); it++) {
223 FireRefreshTokenAvailable(*it);
226 for (std::vector<std::string>::iterator it = revoked_ids.begin();
227 it != revoked_ids.end(); it++) {
228 FireRefreshTokenRevoked(*it);
232 bool AndroidProfileOAuth2TokenService::ValidateAccounts(
233 const std::string& signed_in_account,
234 const std::vector<std::string>& prev_account_ids,
235 const std::vector<std::string>& curr_account_ids,
236 std::vector<std::string>& refreshed_ids,
237 std::vector<std::string>& revoked_ids,
238 bool force_notifications) {
239 if (std::find(curr_account_ids.begin(),
240 curr_account_ids.end(),
241 signed_in_account) != curr_account_ids.end()) {
242 // Test to see if an account is removed from the Android AccountManager.
243 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
244 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
245 it != prev_account_ids.end(); it++) {
246 if (*it == signed_in_account)
249 if (std::find(curr_account_ids.begin(),
250 curr_account_ids.end(),
251 *it) == curr_account_ids.end()) {
252 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
253 << "revoked=" << *it;
254 revoked_ids.push_back(*it);
258 if (force_notifications ||
259 std::find(prev_account_ids.begin(), prev_account_ids.end(),
260 signed_in_account) == prev_account_ids.end()) {
261 // Always fire the primary signed in account first.
262 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
263 << "refreshed=" << signed_in_account;
264 refreshed_ids.push_back(signed_in_account);
267 for (std::vector<std::string>::const_iterator it = curr_account_ids.begin();
268 it != curr_account_ids.end(); it++) {
269 if (*it != signed_in_account) {
270 if (force_notifications ||
271 std::find(prev_account_ids.begin(),
272 prev_account_ids.end(),
273 *it) == prev_account_ids.end()) {
274 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
275 << "refreshed=" << *it;
276 refreshed_ids.push_back(*it);
282 // Currently signed in account does not any longer exist among accounts on
283 // system together with all other accounts.
284 if (std::find(prev_account_ids.begin(), prev_account_ids.end(),
285 signed_in_account) != prev_account_ids.end()) {
286 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
287 << "revoked=" << signed_in_account;
288 revoked_ids.push_back(signed_in_account);
290 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
291 it != prev_account_ids.end(); it++) {
292 if (*it == signed_in_account)
294 VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
295 << "revoked=" << *it;
296 revoked_ids.push_back(*it);
302 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
305 const jstring account_name) {
306 std::string account_id = ConvertJavaStringToUTF8(env, account_name);
307 AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id);
310 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
311 const std::string& account_id) {
312 VLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable id="
315 // Notify native observers.
316 OAuth2TokenService::FireRefreshTokenAvailable(account_id);
317 // Notify Java observers.
318 JNIEnv* env = AttachCurrentThread();
319 ScopedJavaLocalRef<jstring> account_name =
320 ConvertUTF8ToJavaString(env, account_id);
321 Java_OAuth2TokenService_notifyRefreshTokenAvailable(
322 env, java_ref_.obj(), account_name.obj());
325 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
328 const jstring account_name) {
329 std::string account_id = ConvertJavaStringToUTF8(env, account_name);
330 AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id);
333 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
334 const std::string& account_id) {
335 VLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked id="
338 // Notify native observers.
339 OAuth2TokenService::FireRefreshTokenRevoked(account_id);
340 // Notify Java observers.
341 JNIEnv* env = AttachCurrentThread();
342 ScopedJavaLocalRef<jstring> account_name =
343 ConvertUTF8ToJavaString(env, account_id);
344 Java_OAuth2TokenService_notifyRefreshTokenRevoked(
345 env, java_ref_.obj(), account_name.obj());
348 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
351 AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
354 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() {
355 VLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded";
356 // Notify native observers.
357 OAuth2TokenService::FireRefreshTokensLoaded();
358 // Notify Java observers.
359 JNIEnv* env = AttachCurrentThread();
360 Java_OAuth2TokenService_notifyRefreshTokensLoaded(
361 env, java_ref_.obj());
364 void AndroidProfileOAuth2TokenService::RevokeAllCredentials() {
365 VLOG(1) << "AndroidProfileOAuth2TokenService::RevokeAllCredentials";
366 std::vector<std::string> accounts = GetAccounts();
367 for (std::vector<std::string>::iterator it = accounts.begin();
368 it != accounts.end(); it++) {
369 FireRefreshTokenRevoked(*it);
373 // Called from Java when fetching of an OAuth2 token is finished. The
374 // |authToken| param is only valid when |result| is true.
375 void OAuth2TokenFetched(JNIEnv* env, jclass clazz,
378 jlong nativeCallback) {
379 std::string token = ConvertJavaStringToUTF8(env, authToken);
380 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
381 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
382 // Android does not provide enough information to know if the credentials are
383 // wrong, so assume any error is transient by using CONNECTION_FAILED.
384 GoogleServiceAuthError err(result ?
385 GoogleServiceAuthError::NONE :
386 GoogleServiceAuthError::CONNECTION_FAILED);
387 heap_callback->Run(err, token, base::Time());
391 bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
392 return RegisterNativesImpl(env);