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