android_profile_oauth2_token_service.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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/signin/signin_manager.h"
13#include "chrome/browser/signin/signin_manager_factory.h"
14#include "chrome/browser/sync/profile_sync_service_android.h"
15#include "content/public/browser/browser_thread.h"
16#include "jni/AndroidProfileOAuth2TokenServiceHelper_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}
53
54AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {
55}
56
57bool AndroidProfileOAuth2TokenService::RefreshTokenIsAvailable(
58    const std::string& account_id) {
59  JNIEnv* env = AttachCurrentThread();
60  ScopedJavaLocalRef<jstring> j_account_id =
61      ConvertUTF8ToJavaString(env, account_id);
62  jboolean refresh_token_is_available =
63      Java_AndroidProfileOAuth2TokenServiceHelper_hasOAuth2RefreshToken(
64          env, base::android::GetApplicationContext(),
65          j_account_id.obj());
66  return refresh_token_is_available != JNI_FALSE;
67}
68
69std::vector<std::string> AndroidProfileOAuth2TokenService::GetAccounts() {
70  std::vector<std::string> accounts;
71  JNIEnv* env = AttachCurrentThread();
72  ScopedJavaLocalRef<jobjectArray> j_accounts =
73      Java_AndroidProfileOAuth2TokenServiceHelper_getAccounts(
74          env, base::android::GetApplicationContext());
75  // TODO(fgorski): We may decide to filter out some of the accounts.
76  base::android::AppendJavaStringArrayToStringVector(env,
77                                                     j_accounts.obj(),
78                                                     &accounts);
79  return accounts;
80}
81
82void AndroidProfileOAuth2TokenService::FetchOAuth2Token(
83    RequestImpl* request,
84    const std::string& account_id,
85    net::URLRequestContextGetter* getter,
86    const std::string& client_id,
87    const std::string& client_secret,
88    const OAuth2TokenService::ScopeSet& scopes) {
89  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
90  DCHECK(!account_id.empty());
91
92  JNIEnv* env = AttachCurrentThread();
93  std::string scope = CombineScopes(scopes);
94  ScopedJavaLocalRef<jstring> j_username =
95      ConvertUTF8ToJavaString(env, account_id);
96  ScopedJavaLocalRef<jstring> j_scope =
97      ConvertUTF8ToJavaString(env, scope);
98
99  // Allocate a copy of the request WeakPtr on the heap, because the object
100  // needs to be passed through JNI as an int.
101  // It will be passed back to OAuth2TokenFetched(), where it will be freed.
102  scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
103      new FetchOAuth2TokenCallback(base::Bind(&RequestImpl::InformConsumer,
104                                              request->AsWeakPtr())));
105
106  // Call into Java to get a new token.
107  Java_AndroidProfileOAuth2TokenServiceHelper_getOAuth2AuthToken(
108      env, base::android::GetApplicationContext(),
109      j_username.obj(),
110      j_scope.obj(),
111      reinterpret_cast<int>(heap_callback.release()));
112}
113
114void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token(
115    const std::string& account_id,
116    const std::string& client_id,
117    const ScopeSet& scopes,
118    const std::string& access_token) {
119  OAuth2TokenService::InvalidateOAuth2Token(account_id,
120                                            client_id,
121                                            scopes,
122                                            access_token);
123
124  JNIEnv* env = AttachCurrentThread();
125  ScopedJavaLocalRef<jstring> j_access_token =
126      ConvertUTF8ToJavaString(env, access_token);
127  Java_AndroidProfileOAuth2TokenServiceHelper_invalidateOAuth2AuthToken(
128      env, base::android::GetApplicationContext(),
129      j_access_token.obj());
130}
131
132// Called from Java when fetching of an OAuth2 token is finished. The
133// |authToken| param is only valid when |result| is true.
134void OAuth2TokenFetched(JNIEnv* env, jclass clazz,
135    jstring authToken,
136    jboolean result,
137    jint nativeCallback) {
138  std::string token = ConvertJavaStringToUTF8(env, authToken);
139  scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
140      reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
141  GoogleServiceAuthError err(result ?
142                             GoogleServiceAuthError::NONE :
143                             GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
144  heap_callback->Run(err, token, base::Time());
145}
146
147// static
148bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
149  return RegisterNativesImpl(env);
150}
151