android_profile_oauth2_token_service.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 185 if (!ValidateAccounts(signed_in_account, prev_ids, curr_ids)) { 186 curr_ids.clear(); 187 } 188 189 JNIEnv* env = AttachCurrentThread(); 190 ScopedJavaLocalRef<jobjectArray> java_accounts( 191 base::android::ToJavaArrayOfStrings(env, curr_ids)); 192 Java_OAuth2TokenService_saveStoredAccounts( 193 env, base::android::GetApplicationContext(), java_accounts.obj()); 194} 195 196bool AndroidProfileOAuth2TokenService::ValidateAccounts( 197 const std::string& signed_in_account, 198 const std::vector<std::string>& prev_account_ids, 199 const std::vector<std::string>& curr_account_ids) { 200 if (std::find(curr_account_ids.begin(), 201 curr_account_ids.end(), 202 signed_in_account) != curr_account_ids.end()) { 203 // Test to see if an account is removed from the Android AccountManager. 204 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor. 205 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin(); 206 it != prev_account_ids.end(); it++) { 207 if (*it == signed_in_account) 208 continue; 209 210 if (std::find(curr_account_ids.begin(), 211 curr_account_ids.end(), 212 *it) == curr_account_ids.end()) { 213 FireRefreshTokenRevoked(*it); 214 } 215 } 216 217 // Always fire the primary signed in account first. 218 FireRefreshTokenAvailable(signed_in_account); 219 220 for (std::vector<std::string>::const_iterator it = curr_account_ids.begin(); 221 it != curr_account_ids.end(); it++) { 222 if (*it != signed_in_account) { 223 FireRefreshTokenAvailable(*it); 224 } 225 } 226 return true; 227 } else { 228 // Currently signed in account does not any longer exist among accounts on 229 // system together with all other accounts. 230 if (!signed_in_account.empty()) { 231 FireRefreshTokenRevoked(signed_in_account); 232 } 233 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin(); 234 it != prev_account_ids.end(); it++) { 235 if (*it == signed_in_account) 236 continue; 237 FireRefreshTokenRevoked(*it); 238 } 239 return false; 240 } 241} 242 243void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava( 244 JNIEnv* env, 245 jobject obj, 246 const jstring account_name) { 247 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 248 AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id); 249} 250 251void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable( 252 const std::string& account_id) { 253 // Notify native observers. 254 OAuth2TokenService::FireRefreshTokenAvailable(account_id); 255 // Notify Java observers. 256 JNIEnv* env = AttachCurrentThread(); 257 ScopedJavaLocalRef<jstring> account_name = 258 ConvertUTF8ToJavaString(env, account_id); 259 Java_OAuth2TokenService_notifyRefreshTokenAvailable( 260 env, java_ref_.obj(), account_name.obj()); 261} 262 263void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava( 264 JNIEnv* env, 265 jobject obj, 266 const jstring account_name) { 267 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 268 AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id); 269} 270 271void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked( 272 const std::string& account_id) { 273 // Notify native observers. 274 OAuth2TokenService::FireRefreshTokenRevoked(account_id); 275 // Notify Java observers. 276 JNIEnv* env = AttachCurrentThread(); 277 ScopedJavaLocalRef<jstring> account_name = 278 ConvertUTF8ToJavaString(env, account_id); 279 Java_OAuth2TokenService_notifyRefreshTokenRevoked( 280 env, java_ref_.obj(), account_name.obj()); 281} 282 283void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava( 284 JNIEnv* env, 285 jobject obj) { 286 AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded(); 287} 288 289void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() { 290 // Notify native observers. 291 OAuth2TokenService::FireRefreshTokensLoaded(); 292 // Notify Java observers. 293 JNIEnv* env = AttachCurrentThread(); 294 Java_OAuth2TokenService_notifyRefreshTokensLoaded( 295 env, java_ref_.obj()); 296} 297 298void AndroidProfileOAuth2TokenService::RevokeAllCredentials() { 299 std::vector<std::string> accounts = GetAccounts(); 300 for (std::vector<std::string>::iterator it = accounts.begin(); 301 it != accounts.end(); it++) { 302 FireRefreshTokenRevoked(*it); 303 } 304} 305 306// Called from Java when fetching of an OAuth2 token is finished. The 307// |authToken| param is only valid when |result| is true. 308void OAuth2TokenFetched(JNIEnv* env, jclass clazz, 309 jstring authToken, 310 jboolean result, 311 jlong nativeCallback) { 312 std::string token = ConvertJavaStringToUTF8(env, authToken); 313 scoped_ptr<FetchOAuth2TokenCallback> heap_callback( 314 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback)); 315 // Android does not provide enough information to know if the credentials are 316 // wrong, so assume any error is transient by using CONNECTION_FAILED. 317 GoogleServiceAuthError err(result ? 318 GoogleServiceAuthError::NONE : 319 GoogleServiceAuthError::CONNECTION_FAILED); 320 heap_callback->Run(err, token, base::Time()); 321} 322 323// static 324bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) { 325 return RegisterNativesImpl(env); 326} 327