android_profile_oauth2_token_service.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
18using base::android::AttachCurrentThread;
19using base::android::ConvertJavaStringToUTF8;
20using base::android::ConvertUTF8ToJavaString;
21using base::android::ScopedJavaLocalRef;
22using content::BrowserThread;
23
24namespace {
25
26std::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.
45typedef base::Callback<void(
46    const GoogleServiceAuthError&, const std::string&, const base::Time&)>
47        FetchOAuth2TokenCallback;
48
49}  // namespace
50
51AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService() {
52  JNIEnv* env = AttachCurrentThread();
53  base::android::ScopedJavaLocalRef<jobject> local_java_ref =
54      Java_OAuth2TokenService_create(env, reinterpret_cast<intptr_t>(this));
55  java_ref_.Reset(env, local_java_ref.obj());
56}
57
58AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
59
60// static
61jobject AndroidProfileOAuth2TokenService::GetForProfile(
62    JNIEnv* env, jclass clazz, jobject j_profile_android) {
63  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
64  AndroidProfileOAuth2TokenService* service =
65      ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(profile);
66  return service->java_ref_.obj();
67}
68
69static jobject GetForProfile(JNIEnv* env,
70                             jclass clazz,
71                             jobject j_profile_android) {
72  return AndroidProfileOAuth2TokenService::GetForProfile(
73      env, clazz, j_profile_android);
74}
75
76bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable(
77    const std::string& account_id) const {
78  JNIEnv* env = AttachCurrentThread();
79  ScopedJavaLocalRef<jstring> j_account_id =
80      ConvertUTF8ToJavaString(env, account_id);
81  jboolean refresh_token_is_available =
82      Java_OAuth2TokenService_hasOAuth2RefreshToken(
83          env, base::android::GetApplicationContext(),
84          j_account_id.obj());
85  return refresh_token_is_available != JNI_FALSE;
86}
87
88std::vector<std::string> AndroidProfileOAuth2TokenService::GetAccounts() {
89  std::vector<std::string> accounts;
90  JNIEnv* env = AttachCurrentThread();
91  ScopedJavaLocalRef<jobjectArray> j_accounts =
92      Java_OAuth2TokenService_getAccounts(
93          env, base::android::GetApplicationContext());
94  // TODO(fgorski): We may decide to filter out some of the accounts.
95  base::android::AppendJavaStringArrayToStringVector(env,
96                                                     j_accounts.obj(),
97                                                     &accounts);
98  return accounts;
99}
100
101void AndroidProfileOAuth2TokenService::FetchOAuth2Token(
102    RequestImpl* request,
103    const std::string& account_id,
104    net::URLRequestContextGetter* getter,
105    const std::string& client_id,
106    const std::string& client_secret,
107    const OAuth2TokenService::ScopeSet& scopes) {
108  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
109  DCHECK(!account_id.empty());
110
111  JNIEnv* env = AttachCurrentThread();
112  std::string scope = CombineScopes(scopes);
113  ScopedJavaLocalRef<jstring> j_username =
114      ConvertUTF8ToJavaString(env, account_id);
115  ScopedJavaLocalRef<jstring> j_scope =
116      ConvertUTF8ToJavaString(env, scope);
117
118  // Allocate a copy of the request WeakPtr on the heap, because the object
119  // needs to be passed through JNI as an int.
120  // It will be passed back to OAuth2TokenFetched(), where it will be freed.
121  scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
122      new FetchOAuth2TokenCallback(base::Bind(&RequestImpl::InformConsumer,
123                                              request->AsWeakPtr())));
124
125  // Call into Java to get a new token.
126  Java_OAuth2TokenService_getOAuth2AuthToken(
127      env, base::android::GetApplicationContext(),
128      j_username.obj(),
129      j_scope.obj(),
130      reinterpret_cast<intptr_t>(heap_callback.release()));
131}
132
133OAuth2AccessTokenFetcher*
134AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher(
135    const std::string& account_id,
136    net::URLRequestContextGetter* getter,
137    OAuth2AccessTokenConsumer* consumer) {
138  NOTREACHED();
139  return NULL;
140}
141
142void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token(
143    const std::string& account_id,
144    const std::string& client_id,
145    const ScopeSet& scopes,
146    const std::string& access_token) {
147  OAuth2TokenService::InvalidateOAuth2Token(account_id,
148                                            client_id,
149                                            scopes,
150                                            access_token);
151
152  JNIEnv* env = AttachCurrentThread();
153  ScopedJavaLocalRef<jstring> j_access_token =
154      ConvertUTF8ToJavaString(env, access_token);
155  Java_OAuth2TokenService_invalidateOAuth2AuthToken(
156      env, base::android::GetApplicationContext(),
157      j_access_token.obj());
158}
159
160void AndroidProfileOAuth2TokenService::ValidateAccounts(JNIEnv* env,
161                                                        jobject obj,
162                                                        jobjectArray accounts,
163                                                        jstring j_current_acc) {
164  std::vector<std::string> account_ids;
165  base::android::AppendJavaStringArrayToStringVector(env,
166                                                     accounts,
167                                                     &account_ids);
168  std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc);
169  ValidateAccounts(signed_in_account, account_ids);
170}
171
172void AndroidProfileOAuth2TokenService::ValidateAccounts(
173    const std::string& signed_in_account,
174    const std::vector<std::string>& account_ids) {
175  if (signed_in_account.empty())
176    return;
177
178  if (std::find(account_ids.begin(),
179                account_ids.end(),
180                signed_in_account) != account_ids.end()) {
181    // Currently signed in account still exists among accounts on system.
182    std::vector<std::string> ids = GetAccounts();
183
184    // Always fire the primary signed in account first.
185    FireRefreshTokenAvailable(signed_in_account);
186
187    for (std::vector<std::string>::iterator it = ids.begin();
188        it != ids.end(); it++) {
189      if (*it != signed_in_account) {
190        FireRefreshTokenAvailable(*it);
191      }
192    }
193  } else {
194    // Currently signed in account does not any longer exist among accounts on
195    // system.
196    FireRefreshTokenRevoked(signed_in_account);
197  }
198}
199
200void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
201    JNIEnv* env,
202    jobject obj,
203    const jstring account_name) {
204  std::string account_id = ConvertJavaStringToUTF8(env, account_name);
205  AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id);
206}
207
208void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
209    const std::string& account_id) {
210  // Notify native observers.
211  OAuth2TokenService::FireRefreshTokenAvailable(account_id);
212  // Notify Java observers.
213  JNIEnv* env = AttachCurrentThread();
214  ScopedJavaLocalRef<jstring> account_name =
215      ConvertUTF8ToJavaString(env, account_id);
216  Java_OAuth2TokenService_notifyRefreshTokenAvailable(
217      env, java_ref_.obj(), account_name.obj());
218}
219
220void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
221    JNIEnv* env,
222    jobject obj,
223    const jstring account_name) {
224  std::string account_id = ConvertJavaStringToUTF8(env, account_name);
225  AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id);
226}
227
228void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
229    const std::string& account_id) {
230  // Notify native observers.
231  OAuth2TokenService::FireRefreshTokenRevoked(account_id);
232  // Notify Java observers.
233  JNIEnv* env = AttachCurrentThread();
234  ScopedJavaLocalRef<jstring> account_name =
235      ConvertUTF8ToJavaString(env, account_id);
236  Java_OAuth2TokenService_notifyRefreshTokenRevoked(
237      env, java_ref_.obj(), account_name.obj());
238}
239
240void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
241    JNIEnv* env,
242    jobject obj) {
243  AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
244}
245
246void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() {
247  // Notify native observers.
248  OAuth2TokenService::FireRefreshTokensLoaded();
249  // Notify Java observers.
250  JNIEnv* env = AttachCurrentThread();
251  Java_OAuth2TokenService_notifyRefreshTokensLoaded(
252      env, java_ref_.obj());
253}
254
255// Called from Java when fetching of an OAuth2 token is finished. The
256// |authToken| param is only valid when |result| is true.
257void OAuth2TokenFetched(JNIEnv* env, jclass clazz,
258    jstring authToken,
259    jboolean result,
260    jlong nativeCallback) {
261  std::string token = ConvertJavaStringToUTF8(env, authToken);
262  scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
263      reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
264  // Android does not provide enough information to know if the credentials are
265  // wrong, so assume any error is transient by using CONNECTION_FAILED.
266  GoogleServiceAuthError err(result ?
267                             GoogleServiceAuthError::NONE :
268                             GoogleServiceAuthError::CONNECTION_FAILED);
269  heap_callback->Run(err, token, base::Time());
270}
271
272// static
273bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
274  return RegisterNativesImpl(env);
275}
276