android_profile_oauth2_token_service.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 101void AndroidProfileOAuth2TokenService::FetchOAuth2Token( 102 RequestImpl* request, 103 const std::string& account_id, 104 net::URLRequestContextGetter* getter, 105 const std::string& client_id, 106 const std::string& client_secret, 107 const OAuth2TokenService::ScopeSet& scopes) { 108 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 109 DCHECK(!account_id.empty()); 110 111 JNIEnv* env = AttachCurrentThread(); 112 std::string scope = CombineScopes(scopes); 113 ScopedJavaLocalRef<jstring> j_username = 114 ConvertUTF8ToJavaString(env, account_id); 115 ScopedJavaLocalRef<jstring> j_scope = 116 ConvertUTF8ToJavaString(env, scope); 117 118 // Allocate a copy of the request WeakPtr on the heap, because the object 119 // needs to be passed through JNI as an int. 120 // It will be passed back to OAuth2TokenFetched(), where it will be freed. 121 scoped_ptr<FetchOAuth2TokenCallback> heap_callback( 122 new FetchOAuth2TokenCallback(base::Bind(&RequestImpl::InformConsumer, 123 request->AsWeakPtr()))); 124 125 // Call into Java to get a new token. 126 Java_OAuth2TokenService_getOAuth2AuthToken( 127 env, base::android::GetApplicationContext(), 128 j_username.obj(), 129 j_scope.obj(), 130 reinterpret_cast<intptr_t>(heap_callback.release())); 131} 132 133OAuth2AccessTokenFetcher* 134AndroidProfileOAuth2TokenService::CreateAccessTokenFetcher( 135 const std::string& account_id, 136 net::URLRequestContextGetter* getter, 137 OAuth2AccessTokenConsumer* consumer) { 138 NOTREACHED(); 139 return NULL; 140} 141 142void AndroidProfileOAuth2TokenService::InvalidateOAuth2Token( 143 const std::string& account_id, 144 const std::string& client_id, 145 const ScopeSet& scopes, 146 const std::string& access_token) { 147 OAuth2TokenService::InvalidateOAuth2Token(account_id, 148 client_id, 149 scopes, 150 access_token); 151 152 JNIEnv* env = AttachCurrentThread(); 153 ScopedJavaLocalRef<jstring> j_access_token = 154 ConvertUTF8ToJavaString(env, access_token); 155 Java_OAuth2TokenService_invalidateOAuth2AuthToken( 156 env, base::android::GetApplicationContext(), 157 j_access_token.obj()); 158} 159 160void AndroidProfileOAuth2TokenService::ValidateAccounts(JNIEnv* env, 161 jobject obj, 162 jobjectArray accounts, 163 jstring j_current_acc) { 164 std::vector<std::string> account_ids; 165 base::android::AppendJavaStringArrayToStringVector(env, 166 accounts, 167 &account_ids); 168 std::string signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc); 169 ValidateAccounts(signed_in_account, account_ids); 170} 171 172void AndroidProfileOAuth2TokenService::ValidateAccounts( 173 const std::string& signed_in_account, 174 const std::vector<std::string>& account_ids) { 175 if (signed_in_account.empty()) 176 return; 177 178 if (std::find(account_ids.begin(), 179 account_ids.end(), 180 signed_in_account) != account_ids.end()) { 181 // Currently signed in account still exists among accounts on system. 182 std::vector<std::string> ids = GetAccounts(); 183 184 // Always fire the primary signed in account first. 185 FireRefreshTokenAvailable(signed_in_account); 186 187 for (std::vector<std::string>::iterator it = ids.begin(); 188 it != ids.end(); it++) { 189 if (*it != signed_in_account) { 190 FireRefreshTokenAvailable(*it); 191 } 192 } 193 } else { 194 // Currently signed in account does not any longer exist among accounts on 195 // system. 196 FireRefreshTokenRevoked(signed_in_account); 197 } 198} 199 200void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailableFromJava( 201 JNIEnv* env, 202 jobject obj, 203 const jstring account_name) { 204 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 205 AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable(account_id); 206} 207 208void AndroidProfileOAuth2TokenService::FireRefreshTokenAvailable( 209 const std::string& account_id) { 210 // Notify native observers. 211 OAuth2TokenService::FireRefreshTokenAvailable(account_id); 212 // Notify Java observers. 213 JNIEnv* env = AttachCurrentThread(); 214 ScopedJavaLocalRef<jstring> account_name = 215 ConvertUTF8ToJavaString(env, account_id); 216 Java_OAuth2TokenService_notifyRefreshTokenAvailable( 217 env, java_ref_.obj(), account_name.obj()); 218} 219 220void AndroidProfileOAuth2TokenService::FireRefreshTokenRevokedFromJava( 221 JNIEnv* env, 222 jobject obj, 223 const jstring account_name) { 224 std::string account_id = ConvertJavaStringToUTF8(env, account_name); 225 AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked(account_id); 226} 227 228void AndroidProfileOAuth2TokenService::FireRefreshTokenRevoked( 229 const std::string& account_id) { 230 // Notify native observers. 231 OAuth2TokenService::FireRefreshTokenRevoked(account_id); 232 // Notify Java observers. 233 JNIEnv* env = AttachCurrentThread(); 234 ScopedJavaLocalRef<jstring> account_name = 235 ConvertUTF8ToJavaString(env, account_id); 236 Java_OAuth2TokenService_notifyRefreshTokenRevoked( 237 env, java_ref_.obj(), account_name.obj()); 238} 239 240void AndroidProfileOAuth2TokenService::FireRefreshTokensLoadedFromJava( 241 JNIEnv* env, 242 jobject obj) { 243 AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded(); 244} 245 246void AndroidProfileOAuth2TokenService::FireRefreshTokensLoaded() { 247 // Notify native observers. 248 OAuth2TokenService::FireRefreshTokensLoaded(); 249 // Notify Java observers. 250 JNIEnv* env = AttachCurrentThread(); 251 Java_OAuth2TokenService_notifyRefreshTokensLoaded( 252 env, java_ref_.obj()); 253} 254 255// Called from Java when fetching of an OAuth2 token is finished. The 256// |authToken| param is only valid when |result| is true. 257void OAuth2TokenFetched(JNIEnv* env, jclass clazz, 258 jstring authToken, 259 jboolean result, 260 jlong nativeCallback) { 261 std::string token = ConvertJavaStringToUTF8(env, authToken); 262 scoped_ptr<FetchOAuth2TokenCallback> heap_callback( 263 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback)); 264 // Android does not provide enough information to know if the credentials are 265 // wrong, so assume any error is transient by using CONNECTION_FAILED. 266 GoogleServiceAuthError err(result ? 267 GoogleServiceAuthError::NONE : 268 GoogleServiceAuthError::CONNECTION_FAILED); 269 heap_callback->Run(err, token, base::Time()); 270} 271 272// static 273bool AndroidProfileOAuth2TokenService::Register(JNIEnv* env) { 274 return RegisterNativesImpl(env); 275} 276