cb2dea71834a658cafb506030c836a5e2b44fbc8
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / android_profile_oauth2_token_service.cc
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.
4
5 #include "chrome/browser/signin/android_profile_oauth2_token_service.h"
6
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"
17
18 using base::android::AttachCurrentThread;
19 using base::android::ConvertJavaStringToUTF8;
20 using base::android::ConvertUTF8ToJavaString;
21 using base::android::ScopedJavaLocalRef;
22 using content::BrowserThread;
23
24 namespace {
25
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
29   std::string scope;
30   for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
31        it != scopes.end(); ++it) {
32     if (!scope.empty())
33       scope += " ";
34     scope += *it;
35   }
36   return scope;
37 }
38
39 // Callback from FetchOAuth2TokenWithUsername().
40 // Arguments:
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
44 //   time is unknown.
45 typedef base::Callback<void(
46     const GoogleServiceAuthError&, const std::string&, const base::Time&)>
47         FetchOAuth2TokenCallback;
48
49 }  // namespace
50
51 bool AndroidProfileOAuth2TokenService::is_testing_profile_ = false;
52
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());
59 }
60
61 AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
62
63 // static
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();
70 }
71
72 static jobject GetForProfile(JNIEnv* env,
73                              jclass clazz,
74                              jobject j_profile_android) {
75   return AndroidProfileOAuth2TokenService::GetForProfile(
76       env, clazz, j_profile_android);
77 }
78
79 void AndroidProfileOAuth2TokenService::Initialize(SigninClient* client) {
80   VLOG(1) << "AndroidProfileOAuth2TokenService::Initialize";
81   ProfileOAuth2TokenService::Initialize(client);
82
83   if (!is_testing_profile_) {
84     Java_OAuth2TokenService_validateAccounts(
85         AttachCurrentThread(), java_ref_.obj(),
86         base::android::GetApplicationContext(), JNI_TRUE);
87   }
88 }
89
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(),
98           j_account_id.obj());
99   return refresh_token_is_available == JNI_TRUE;
100 }
101
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,
110                                                      j_accounts.obj(),
111                                                      &accounts);
112   return accounts;
113 }
114
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,
122                                                      j_accounts.obj(),
123                                                      &accounts);
124   return accounts;
125 }
126
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());
136
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);
143
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())));
150
151   // Call into Java to get a new token.
152   Java_OAuth2TokenService_getOAuth2AuthToken(
153       env, base::android::GetApplicationContext(),
154       j_username.obj(),
155       j_scope.obj(),
156       reinterpret_cast<intptr_t>(heap_callback.release()));
157 }
158
159 OAuth2AccessTokenFetcher*
160 AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher(
161     const std::string& account_id,
162     net::URLRequestContextGetter* getter,
163     OAuth2AccessTokenConsumer* consumer) {
164   NOTREACHED();
165   return NULL;
166 }
167
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,
174                                             client_id,
175                                             scopes,
176                                             access_token);
177
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());
184 }
185
186 void AndroidProfileOAuth2TokenService::ValidateAccounts(
187     JNIEnv* env,
188     jobject obj,
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);
194 }
195
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;
203
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");
209
210   if (!ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids,
211                         revoked_ids, force_notifications)) {
212     curr_ids.clear();
213   }
214
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());
220
221   for (std::vector<std::string>::iterator it = refreshed_ids.begin();
222        it != refreshed_ids.end(); it++) {
223     FireRefreshTokenAvailable(*it);
224   }
225
226   for (std::vector<std::string>::iterator it = revoked_ids.begin();
227        it != revoked_ids.end(); it++) {
228     FireRefreshTokenRevoked(*it);
229   }
230 }
231
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)
247         continue;
248
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);
255       }
256     }
257
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);
265     }
266
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);
277         }
278       }
279     }
280     return true;
281   } else {
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);
289     }
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)
293         continue;
294       VLOG(1) << "AndroidProfileOAuth2TokenService::ValidateAccounts:"
295               << "revoked=" << *it;
296       revoked_ids.push_back(*it);
297     }
298     return false;
299   }
300 }
301
302 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
303     JNIEnv* env,
304     jobject obj,
305     const jstring account_name) {
306   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
307   AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id);
308 }
309
310 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
311     const std::string& account_id) {
312   VLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable id="
313           << account_id;
314
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());
323 }
324
325 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
326     JNIEnv* env,
327     jobject obj,
328     const jstring account_name) {
329   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
330   AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id);
331 }
332
333 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
334     const std::string& account_id) {
335   VLOG(1) << "AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked id="
336           << account_id;
337
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());
346 }
347
348 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
349     JNIEnv* env,
350     jobject obj) {
351   AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
352 }
353
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());
362 }
363
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);
370   }
371 }
372
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,
376     jstring authToken,
377     jboolean result,
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());
388 }
389
390 // static
391 bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
392   return RegisterNativesImpl(env);
393 }