Home | History | Annotate | Download | only in signin
      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 
     20 using base::android::AttachCurrentThread;
     21 using base::android::ConvertJavaStringToUTF8;
     22 using base::android::ConvertUTF8ToJavaString;
     23 using base::android::ScopedJavaLocalRef;
     24 using content::BrowserThread;
     25 
     26 namespace {
     27 
     28 std::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.
     47 typedef base::Callback<void(
     48     const GoogleServiceAuthError&, const std::string&, const base::Time&)>
     49         FetchOAuth2TokenCallback;
     50 
     51 }  // namespace
     52 
     53 AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService() {
     54   JNIEnv* env = AttachCurrentThread();
     55   base::android::ScopedJavaLocalRef<jobject> local_java_ref =
     56       Java_OAuth2TokenService_create(env, reinterpret_cast<intptr_t>(this));
     57   java_ref_.Reset(env, local_java_ref.obj());
     58 }
     59 
     60 AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() {}
     61 
     62 // static
     63 jobject AndroidProfileOAuth2TokenService::GetForProfile(
     64     JNIEnv* env, jclass clazz, jobject j_profile_android) {
     65   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
     66   AndroidProfileOAuth2TokenService* service =
     67       ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(profile);
     68   return service->java_ref_.obj();
     69 }
     70 
     71 static jobject GetForProfile(JNIEnv* env,
     72                              jclass clazz,
     73                              jobject j_profile_android) {
     74   return AndroidProfileOAuth2TokenService::GetForProfile(
     75       env, clazz, j_profile_android);
     76 }
     77 
     78 bool 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 
     90 std::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 
    103 void 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<intptr_t>(heap_callback.release()));
    133 }
    134 
    135 void 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 
    153 void 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   ValidateAccounts(signed_in_account, account_ids);
    163 }
    164 
    165 void AndroidProfileOAuth2TokenService::ValidateAccounts(
    166     const std::string& signed_in_account,
    167     const std::vector<std::string>& account_ids) {
    168   if (signed_in_account.empty())
    169     return;
    170 
    171   if (std::find(account_ids.begin(),
    172                 account_ids.end(),
    173                 signed_in_account) != account_ids.end()) {
    174     // Currently signed in account still exists among accounts on system.
    175     std::vector<std::string> ids = GetAccounts();
    176 
    177     // Always fire the primary signed in account first.
    178     FireRefreshTokenAvailable(signed_in_account);
    179 
    180     for (std::vector<std::string>::iterator it = ids.begin();
    181         it != ids.end(); it++) {
    182       if (*it != signed_in_account) {
    183         FireRefreshTokenAvailable(*it);
    184       }
    185     }
    186   } else {
    187     // Currently signed in account does not any longer exist among accounts on
    188     // system.
    189     FireRefreshTokenRevoked(signed_in_account);
    190   }
    191 }
    192 
    193 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava(
    194     JNIEnv* env,
    195     jobject obj,
    196     const jstring account_name) {
    197   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
    198   AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id);
    199 }
    200 
    201 void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(
    202     const std::string& account_id) {
    203   // Notify native observers.
    204   OAuth2TokenService::FireRefreshTokenAvailable(account_id);
    205   // Notify Java observers.
    206   JNIEnv* env = AttachCurrentThread();
    207   ScopedJavaLocalRef<jstring> account_name =
    208       ConvertUTF8ToJavaString(env, account_id);
    209   Java_OAuth2TokenService_notifyRefreshTokenAvailable(
    210       env, java_ref_.obj(), account_name.obj());
    211 }
    212 
    213 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava(
    214     JNIEnv* env,
    215     jobject obj,
    216     const jstring account_name) {
    217   std::string account_id = ConvertJavaStringToUTF8(env, account_name);
    218   AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id);
    219 }
    220 
    221 void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(
    222     const std::string& account_id) {
    223   // Notify native observers.
    224   OAuth2TokenService::FireRefreshTokenRevoked(account_id);
    225   // Notify Java observers.
    226   JNIEnv* env = AttachCurrentThread();
    227   ScopedJavaLocalRef<jstring> account_name =
    228       ConvertUTF8ToJavaString(env, account_id);
    229   Java_OAuth2TokenService_notifyRefreshTokenRevoked(
    230       env, java_ref_.obj(), account_name.obj());
    231 }
    232 
    233 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava(
    234     JNIEnv* env,
    235     jobject obj) {
    236   AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded();
    237 }
    238 
    239 void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() {
    240   // Notify native observers.
    241   OAuth2TokenService::FireRefreshTokensLoaded();
    242   // Notify Java observers.
    243   JNIEnv* env = AttachCurrentThread();
    244   Java_OAuth2TokenService_notifyRefreshTokensLoaded(
    245       env, java_ref_.obj());
    246 }
    247 
    248 // Called from Java when fetching of an OAuth2 token is finished. The
    249 // |authToken| param is only valid when |result| is true.
    250 void OAuth2TokenFetched(JNIEnv* env, jclass clazz,
    251     jstring authToken,
    252     jboolean result,
    253     jlong nativeCallback) {
    254   std::string token = ConvertJavaStringToUTF8(env, authToken);
    255   scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
    256       reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
    257   // Android does not provide enough information to know if the credentials are
    258   // wrong, so assume any error is transient by using CONNECTION_FAILED.
    259   GoogleServiceAuthError err(result ?
    260                              GoogleServiceAuthError::NONE :
    261                              GoogleServiceAuthError::CONNECTION_FAILED);
    262   heap_callback->Run(err, token, base::Time());
    263 }
    264 
    265 // static
    266 bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
    267   return RegisterNativesImpl(env);
    268 }
    269