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/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   // Clear everything on the Java side as well.
    373   std::vector<std::string> empty;
    374   JNIEnv* env = AttachCurrentThread();
    375   ScopedJavaLocalRef<jobjectArray> java_accounts(
    376       base::android::ToJavaArrayOfStrings(env, empty));
    377   Java_OAuth2TokenService_saveStoredAccounts(
    378       env, base::android::GetApplicationContext(), java_accounts.obj());
    379 }
    380 
    381 // Called from Java when fetching of an OAuth2 token is finished. The
    382 // |authToken| param is only valid when |result| is true.
    383 void OAuth2TokenFetched(JNIEnv* env, jclass clazz,
    384     jstring authToken,
    385     jboolean result,
    386     jlong nativeCallback) {
    387   std::string token = ConvertJavaStringToUTF8(env, authToken);
    388   scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
    389       reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
    390   // Android does not provide enough information to know if the credentials are
    391   // wrong, so assume any error is transient by using CONNECTION_FAILED.
    392   GoogleServiceAuthError err(result ?
    393                              GoogleServiceAuthError::NONE :
    394                              GoogleServiceAuthError::CONNECTION_FAILED);
    395   heap_callback->Run(err, token, base::Time());
    396 }
    397 
    398 // static
    399 bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) {
    400   return RegisterNativesImpl(env);
    401 }
    402