android_profile_oauth2_token_service.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 101std::vector<std::string> AndroidProfileOAuth2TokenService::GetSystemAccounts() { 102 std::vector<std::string> accounts; 103 JNIEnv* env = AttachCurrentThread(); 104 ScopedJavaLocalRef<jobjectArray> j_accounts = 105 Java_OAuth2TokenService_getSystemAccounts( 106 env, base::android::GetApplicationContext()); 107 base::android::AppendJavaStringArrayToStringVector(env, 108 j_accounts.obj(), 109 &accounts); 110 return accounts; 111} 112 113void AndroidProfileOAuth2TokenService::FetchOAuth2Token( 114 RequestImpl* request, 115 const std::string& account_id, 116 net::URLRequestContextGetter* getter, 117 const std::string& client_id, 118 const std::string& client_secret, 119 const OAuth2TokenService::ScopeSet& scopes) { 120 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 121 DCHECK(!account_id.empty()); 122 123 JNIEnv* env = AttachCurrentThread(); 124 std::string scope = CombineScopes(scopes); 125 ScopedJavaLocalRef<jstring> j_username = 126 ConvertUTF8ToJavaString(env, account_id); 127 ScopedJavaLocalRef<jstring> j_scope = 128 ConvertUTF8ToJavaString(env, scope); 129 130 // Allocate a copy of the request WeakPtr on the heap, because the object 131 // needs to be passed through JNI as an int. 132 // It will be passed back to OAuth2TokenFetched(), where it will be freed. 133 scoped_ptr<FetchOAuth2TokenCallback> heap_callback( 134 new FetchOAuth2TokenCallback(base::Bind(&RequestImpl::InformConsumer, 135 request->AsWeakPtr()))); 136 137 // Call into Java to get a new token. 138 Java_OAuth2TokenService_getOAuth2AuthToken( 139 env, base::android::GetApplicationContext(), 140 j_username.obj(), 141 j_scope.obj(), 142 reinterpret_cast<intptr_t>(heap_callback.release())); 143} 144 145OAuth2AccessTokenFetcher* 146AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher( 147 const std::string& account_id, 148 net::URLRequestContextGetter* getter, 149 OAuth2AccessTokenConsumer* consumer) { 150 NOTREACHED(); 151 return NULL; 152} 153 154void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token( 155 const std::string& account_id, 156 const std::string& client_id, 157 const ScopeSet& scopes, 158 const std::string& access_token) { 159 OAuth2TokenService::InvalidateOAuth2Token(account_id, 160 client_id, 161 scopes, 162 access_token); 163 164 JNIEnv* env = AttachCurrentThread(); 165 ScopedJavaLocalRef<jstring> j_access_token = 166 ConvertUTF8ToJavaString(env, access_token); 167 Java_OAuth2TokenService_invalidateOAuth2AuthToken( 168 env, base::android::GetApplicationContext(), 169 j_access_token.obj()); 170} 171 172void AndroidProfileOAuth2TokenService::ValidateAccounts( 173 JNIEnv* env, 174 jobject obj, 175 jstring j_current_acc) { 176 std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc); 177 ValidateAccounts(signed_in_account); 178} 179 180void AndroidProfileOAuth2TokenService::ValidateAccounts( 181 const std::string& signed_in_account) { 182 std::vector<std::string> prev_ids = GetAccounts(); 183 std::vector<std::string> curr_ids = GetSystemAccounts(); 184 std::vector<std::string> refreshed_ids; 185 std::vector<std::string> revoked_ids; 186 187 if (!ValidateAccounts( 188 signed_in_account, prev_ids, curr_ids, refreshed_ids, revoked_ids)) { 189 curr_ids.clear(); 190 } 191 192 JNIEnv* env = AttachCurrentThread(); 193 ScopedJavaLocalRef<jobjectArray> java_accounts( 194 base::android::ToJavaArrayOfStrings(env, curr_ids)); 195 Java_OAuth2TokenService_saveStoredAccounts( 196 env, base::android::GetApplicationContext(), java_accounts.obj()); 197 198 for (std::vector<std::string>::iterator it = refreshed_ids.begin(); 199 it != refreshed_ids.end(); it++) { 200 FireRefreshTokenAvailable(*it); 201 } 202 203 for (std::vector<std::string>::iterator it = revoked_ids.begin(); 204 it != revoked_ids.end(); it++) { 205 FireRefreshTokenRevoked(*it); 206 } 207} 208 209bool AndroidProfileOAuth2TokenService::ValidateAccounts( 210 const std::string& signed_in_account, 211 const std::vector<std::string>& prev_account_ids, 212 const std::vector<std::string>& curr_account_ids, 213 std::vector<std::string>& refreshed_ids, 214 std::vector<std::string>& revoked_ids) { 215 if (std::find(curr_account_ids.begin(), 216 curr_account_ids.end(), 217 signed_in_account) != curr_account_ids.end()) { 218 // Test to see if an account is removed from the Android AccountManager. 219 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor. 220 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin(); 221 it != prev_account_ids.end(); it++) { 222 if (*it == signed_in_account) 223 continue; 224 225 if (std::find(curr_account_ids.begin(), 226 curr_account_ids.end(), 227 *it) == curr_account_ids.end()) { 228 revoked_ids.push_back(*it); 229 } 230 } 231 232 // Always fire the primary signed in account first. 233 refreshed_ids.push_back(signed_in_account); 234 235 for (std::vector<std::string>::const_iterator it = curr_account_ids.begin(); 236 it != curr_account_ids.end(); it++) { 237 if (*it != signed_in_account) { 238 refreshed_ids.push_back(*it); 239 } 240 } 241 return true; 242 } else { 243 // Currently signed in account does not any longer exist among accounts on 244 // system together with all other accounts. 245 if (!signed_in_account.empty()) { 246 revoked_ids.push_back(signed_in_account); 247 } 248 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin(); 249 it != prev_account_ids.end(); it++) { 250 if (*it == signed_in_account) 251 continue; 252 revoked_ids.push_back(*it); 253 } 254 return false; 255 } 256} 257 258void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava( 259 JNIEnv* env, 260 jobject obj, 261 const jstring account_name) { 262 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 263 AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id); 264} 265 266void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable( 267 const std::string& account_id) { 268 // Notify native observers. 269 OAuth2TokenService::FireRefreshTokenAvailable(account_id); 270 // Notify Java observers. 271 JNIEnv* env = AttachCurrentThread(); 272 ScopedJavaLocalRef<jstring> account_name = 273 ConvertUTF8ToJavaString(env, account_id); 274 Java_OAuth2TokenService_notifyRefreshTokenAvailable( 275 env, java_ref_.obj(), account_name.obj()); 276} 277 278void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava( 279 JNIEnv* env, 280 jobject obj, 281 const jstring account_name) { 282 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 283 AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id); 284} 285 286void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked( 287 const std::string& account_id) { 288 // Notify native observers. 289 OAuth2TokenService::FireRefreshTokenRevoked(account_id); 290 // Notify Java observers. 291 JNIEnv* env = AttachCurrentThread(); 292 ScopedJavaLocalRef<jstring> account_name = 293 ConvertUTF8ToJavaString(env, account_id); 294 Java_OAuth2TokenService_notifyRefreshTokenRevoked( 295 env, java_ref_.obj(), account_name.obj()); 296} 297 298void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava( 299 JNIEnv* env, 300 jobject obj) { 301 AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded(); 302} 303 304void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() { 305 // Notify native observers. 306 OAuth2TokenService::FireRefreshTokensLoaded(); 307 // Notify Java observers. 308 JNIEnv* env = AttachCurrentThread(); 309 Java_OAuth2TokenService_notifyRefreshTokensLoaded( 310 env, java_ref_.obj()); 311} 312 313void AndroidProfileOAuth2TokenService::RevokeAllCredentials() { 314 std::vector<std::string> accounts = GetAccounts(); 315 for (std::vector<std::string>::iterator it = accounts.begin(); 316 it != accounts.end(); it++) { 317 FireRefreshTokenRevoked(*it); 318 } 319} 320 321// Called from Java when fetching of an OAuth2 token is finished. The 322// |authToken| param is only valid when |result| is true. 323void OAuth2TokenFetched(JNIEnv* env, jclass clazz, 324 jstring authToken, 325 jboolean result, 326 jlong nativeCallback) { 327 std::string token = ConvertJavaStringToUTF8(env, authToken); 328 scoped_ptr<FetchOAuth2TokenCallback> heap_callback( 329 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback)); 330 // Android does not provide enough information to know if the credentials are 331 // wrong, so assume any error is transient by using CONNECTION_FAILED. 332 GoogleServiceAuthError err(result ? 333 GoogleServiceAuthError::NONE : 334 GoogleServiceAuthError::CONNECTION_FAILED); 335 heap_callback->Run(err, token, base::Time()); 336} 337 338// static 339bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) { 340 return RegisterNativesImpl(env); 341} 342