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