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