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/sync/profile_sync_service_android.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/bind.h" 10 #include "base/i18n/time_formatting.h" 11 #include "base/json/json_writer.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/time/time.h" 17 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/chrome_notification_types.h" 19 #include "chrome/browser/profiles/profile_manager.h" 20 #include "chrome/browser/signin/signin_manager_factory.h" 21 #include "chrome/browser/sync/about_sync_util.h" 22 #include "chrome/browser/sync/profile_sync_service.h" 23 #include "chrome/browser/sync/profile_sync_service_factory.h" 24 #include "chrome/browser/sync/sync_ui_util.h" 25 #include "components/signin/core/browser/signin_manager.h" 26 #include "components/sync_driver/pref_names.h" 27 #include "components/sync_driver/sync_prefs.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/browser/notification_source.h" 31 #include "google/cacheinvalidation/types.pb.h" 32 #include "google_apis/gaia/gaia_constants.h" 33 #include "google_apis/gaia/google_service_auth_error.h" 34 #include "grit/generated_resources.h" 35 #include "jni/ProfileSyncService_jni.h" 36 #include "sync/internal_api/public/read_transaction.h" 37 #include "sync/notifier/object_id_invalidation_map.h" 38 #include "ui/base/l10n/l10n_util.h" 39 40 using base::android::AttachCurrentThread; 41 using base::android::CheckException; 42 using base::android::ConvertJavaStringToUTF8; 43 using base::android::ConvertUTF8ToJavaString; 44 using base::android::ScopedJavaLocalRef; 45 using content::BrowserThread; 46 47 namespace { 48 49 enum { 50 #define DEFINE_MODEL_TYPE_SELECTION(name,value) name = value, 51 #include "chrome/browser/sync/profile_sync_service_model_type_selection_android.h" 52 #undef DEFINE_MODEL_TYPE_SELECTION 53 }; 54 55 } // namespace 56 57 ProfileSyncServiceAndroid::ProfileSyncServiceAndroid(JNIEnv* env, jobject obj) 58 : profile_(NULL), 59 sync_service_(NULL), 60 weak_java_profile_sync_service_(env, obj) { 61 if (g_browser_process == NULL || 62 g_browser_process->profile_manager() == NULL) { 63 NOTREACHED() << "Browser process or profile manager not initialized"; 64 return; 65 } 66 67 profile_ = ProfileManager::GetActiveUserProfile(); 68 if (profile_ == NULL) { 69 NOTREACHED() << "Sync Init: Profile not found."; 70 return; 71 } 72 73 sync_prefs_.reset(new sync_driver::SyncPrefs(profile_->GetPrefs())); 74 75 sync_service_ = 76 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); 77 DCHECK(sync_service_); 78 } 79 80 void ProfileSyncServiceAndroid::Init() { 81 sync_service_->AddObserver(this); 82 } 83 84 void ProfileSyncServiceAndroid::RemoveObserver() { 85 if (sync_service_->HasObserver(this)) { 86 sync_service_->RemoveObserver(this); 87 } 88 } 89 90 ProfileSyncServiceAndroid::~ProfileSyncServiceAndroid() { 91 RemoveObserver(); 92 } 93 94 void ProfileSyncServiceAndroid::SendNudgeNotification( 95 int object_source, 96 const std::string& str_object_id, 97 int64 version, 98 const std::string& state) { 99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 100 101 // TODO(nileshagrawal): Merge this with ChromeInvalidationClient::Invalidate. 102 // Construct the ModelTypeStateMap and send it over with the notification. 103 invalidation::ObjectId object_id( 104 object_source, 105 str_object_id); 106 syncer::ObjectIdInvalidationMap object_ids_with_states; 107 if (version == ipc::invalidation::Constants::UNKNOWN) { 108 object_ids_with_states.Insert( 109 syncer::Invalidation::InitUnknownVersion(object_id)); 110 } else { 111 ObjectIdVersionMap::iterator it = 112 max_invalidation_versions_.find(object_id); 113 if ((it != max_invalidation_versions_.end()) && 114 (version <= it->second)) { 115 DVLOG(1) << "Dropping redundant invalidation with version " << version; 116 return; 117 } 118 max_invalidation_versions_[object_id] = version; 119 object_ids_with_states.Insert( 120 syncer::Invalidation::Init(object_id, version, state)); 121 } 122 123 content::NotificationService::current()->Notify( 124 chrome::NOTIFICATION_SYNC_REFRESH_REMOTE, 125 content::Source<Profile>(profile_), 126 content::Details<const syncer::ObjectIdInvalidationMap>( 127 &object_ids_with_states)); 128 } 129 130 void ProfileSyncServiceAndroid::OnStateChanged() { 131 // Notify the java world that our sync state has changed. 132 JNIEnv* env = AttachCurrentThread(); 133 Java_ProfileSyncService_syncStateChanged( 134 env, weak_java_profile_sync_service_.get(env).obj()); 135 } 136 137 void ProfileSyncServiceAndroid::EnableSync(JNIEnv* env, jobject) { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 139 // Don't need to do anything if we're already enabled. 140 if (sync_prefs_->IsStartSuppressed()) 141 sync_service_->UnsuppressAndStart(); 142 else 143 DVLOG(2) << "Ignoring call to EnableSync() because sync is already enabled"; 144 } 145 146 void ProfileSyncServiceAndroid::DisableSync(JNIEnv* env, jobject) { 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 148 // Don't need to do anything if we're already disabled. 149 if (!sync_prefs_->IsStartSuppressed()) { 150 sync_service_->StopAndSuppress(); 151 } else { 152 DVLOG(2) 153 << "Ignoring call to DisableSync() because sync is already disabled"; 154 } 155 } 156 157 void ProfileSyncServiceAndroid::SignInSync(JNIEnv* env, jobject) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159 // Just return if sync already has everything it needs to start up (sync 160 // should start up automatically as long as it has credentials). This can 161 // happen normally if (for example) the user closes and reopens the sync 162 // settings window quickly during initial startup. 163 if (sync_service_->IsSyncEnabledAndLoggedIn() && 164 sync_service_->IsOAuthRefreshTokenAvailable() && 165 sync_service_->HasSyncSetupCompleted()) { 166 return; 167 } 168 169 // Enable sync (if we don't have credentials yet, this will enable sync but 170 // will not start it up - sync will start once credentials arrive). 171 sync_service_->UnsuppressAndStart(); 172 } 173 174 void ProfileSyncServiceAndroid::SignOutSync(JNIEnv* env, jobject) { 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 176 DCHECK(profile_); 177 sync_service_->DisableForUser(); 178 179 // Need to clear suppress start flag manually 180 sync_prefs_->SetStartSuppressed(false); 181 } 182 183 ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::QuerySyncStatusSummary( 184 JNIEnv* env, jobject) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 DCHECK(profile_); 187 std::string status(sync_service_->QuerySyncStatusSummaryString()); 188 return ConvertUTF8ToJavaString(env, status); 189 } 190 191 jboolean ProfileSyncServiceAndroid::SetSyncSessionsId( 192 JNIEnv* env, jobject obj, jstring tag) { 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 194 DCHECK(profile_); 195 std::string machine_tag = ConvertJavaStringToUTF8(env, tag); 196 sync_prefs_->SetSyncSessionsGUID(machine_tag); 197 return true; 198 } 199 200 jint ProfileSyncServiceAndroid::GetAuthError(JNIEnv* env, jobject) { 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 202 return sync_service_->GetAuthError().state(); 203 } 204 205 jboolean ProfileSyncServiceAndroid::IsEncryptEverythingEnabled( 206 JNIEnv* env, jobject) { 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 208 return sync_service_->EncryptEverythingEnabled(); 209 } 210 211 jboolean ProfileSyncServiceAndroid::IsSyncInitialized(JNIEnv* env, jobject) { 212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 213 return sync_service_->sync_initialized(); 214 } 215 216 jboolean ProfileSyncServiceAndroid::IsFirstSetupInProgress( 217 JNIEnv* env, jobject) { 218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 219 return sync_service_->FirstSetupInProgress(); 220 } 221 222 jboolean ProfileSyncServiceAndroid::IsPassphraseRequired(JNIEnv* env, jobject) { 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 return sync_service_->IsPassphraseRequired(); 225 } 226 227 jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForDecryption( 228 JNIEnv* env, jobject obj) { 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 230 // In case of CUSTOM_PASSPHRASE we always sync passwords. Prompt the user for 231 // a passphrase if cryptographer has any pending keys. 232 if (sync_service_->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE) { 233 return !IsCryptographerReady(env, obj); 234 } 235 if (sync_service_->IsPassphraseRequiredForDecryption()) { 236 // Passwords datatype should never prompt for a passphrase, except when 237 // user is using a custom passphrase. Do not prompt for a passphrase if 238 // passwords are the only encrypted datatype. This prevents a temporary 239 // notification for passphrase when PSS has not completed configuring 240 // DataTypeManager, after configuration password datatype shall be disabled. 241 const syncer::ModelTypeSet encrypted_types = 242 sync_service_->GetEncryptedDataTypes(); 243 return !encrypted_types.Equals(syncer::ModelTypeSet(syncer::PASSWORDS)); 244 } 245 return false; 246 } 247 248 jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForExternalType( 249 JNIEnv* env, jobject) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 251 return 252 sync_service_->passphrase_required_reason() == syncer::REASON_DECRYPTION; 253 } 254 255 jboolean ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase( 256 JNIEnv* env, jobject) { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258 return sync_service_->IsUsingSecondaryPassphrase(); 259 } 260 261 jboolean ProfileSyncServiceAndroid::SetDecryptionPassphrase( 262 JNIEnv* env, jobject obj, jstring passphrase) { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 264 std::string key = ConvertJavaStringToUTF8(env, passphrase); 265 return sync_service_->SetDecryptionPassphrase(key); 266 } 267 268 void ProfileSyncServiceAndroid::SetEncryptionPassphrase( 269 JNIEnv* env, jobject obj, jstring passphrase, jboolean is_gaia) { 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 std::string key = ConvertJavaStringToUTF8(env, passphrase); 272 sync_service_->SetEncryptionPassphrase( 273 key, 274 is_gaia ? ProfileSyncService::IMPLICIT : ProfileSyncService::EXPLICIT); 275 } 276 277 jboolean ProfileSyncServiceAndroid::IsCryptographerReady(JNIEnv* env, jobject) { 278 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 279 return sync_service_->IsCryptographerReady(&trans); 280 } 281 282 jint ProfileSyncServiceAndroid::GetPassphraseType(JNIEnv* env, jobject) { 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 284 return sync_service_->GetPassphraseType(); 285 } 286 287 jboolean ProfileSyncServiceAndroid::HasExplicitPassphraseTime( 288 JNIEnv* env, jobject) { 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 290 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); 291 return !passphrase_time.is_null(); 292 } 293 294 ScopedJavaLocalRef<jstring> 295 ProfileSyncServiceAndroid::GetSyncEnterGooglePassphraseBodyWithDateText( 296 JNIEnv* env, jobject) { 297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 298 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); 299 base::string16 passphrase_time_str = 300 base::TimeFormatShortDate(passphrase_time); 301 return base::android::ConvertUTF16ToJavaString(env, 302 l10n_util::GetStringFUTF16( 303 IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE, 304 passphrase_time_str)); 305 } 306 307 ScopedJavaLocalRef<jstring> 308 ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyWithDateText( 309 JNIEnv* env, jobject) { 310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 311 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime(); 312 base::string16 passphrase_time_str = 313 base::TimeFormatShortDate(passphrase_time); 314 return base::android::ConvertUTF16ToJavaString(env, 315 l10n_util::GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE, 316 passphrase_time_str)); 317 } 318 319 ScopedJavaLocalRef<jstring> 320 ProfileSyncServiceAndroid::GetCurrentSignedInAccountText( 321 JNIEnv* env, jobject) { 322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 323 const std::string& sync_username = 324 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername(); 325 return base::android::ConvertUTF16ToJavaString(env, 326 l10n_util::GetStringFUTF16( 327 IDS_SYNC_ACCOUNT_SYNCING_TO_USER, 328 base::ASCIIToUTF16(sync_username))); 329 } 330 331 ScopedJavaLocalRef<jstring> 332 ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyText( 333 JNIEnv* env, jobject) { 334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 335 return ConvertUTF8ToJavaString( 336 env, l10n_util::GetStringUTF8(IDS_SYNC_ENTER_PASSPHRASE_BODY)); 337 } 338 339 jboolean ProfileSyncServiceAndroid::IsSyncKeystoreMigrationDone( 340 JNIEnv* env, jobject) { 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 342 syncer::SyncStatus status; 343 bool is_status_valid = sync_service_->QueryDetailedSyncStatus(&status); 344 return is_status_valid && !status.keystore_migration_time.is_null(); 345 } 346 347 jlong ProfileSyncServiceAndroid::GetEnabledDataTypes(JNIEnv* env, 348 jobject obj) { 349 jlong model_type_selection = 0; 350 syncer::ModelTypeSet types = sync_service_->GetActiveDataTypes(); 351 types.PutAll(syncer::ControlTypes()); 352 if (types.Has(syncer::BOOKMARKS)) { 353 model_type_selection |= BOOKMARK; 354 } 355 if (types.Has(syncer::AUTOFILL)) { 356 model_type_selection |= AUTOFILL; 357 } 358 if (types.Has(syncer::AUTOFILL_PROFILE)) { 359 model_type_selection |= AUTOFILL_PROFILE; 360 } 361 if (types.Has(syncer::PASSWORDS)) { 362 model_type_selection |= PASSWORD; 363 } 364 if (types.Has(syncer::TYPED_URLS)) { 365 model_type_selection |= TYPED_URL; 366 } 367 if (types.Has(syncer::SESSIONS)) { 368 model_type_selection |= SESSION; 369 } 370 if (types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) { 371 model_type_selection |= HISTORY_DELETE_DIRECTIVE; 372 } 373 if (types.Has(syncer::PROXY_TABS)) { 374 model_type_selection |= PROXY_TABS; 375 } 376 if (types.Has(syncer::FAVICON_IMAGES)) { 377 model_type_selection |= FAVICON_IMAGE; 378 } 379 if (types.Has(syncer::FAVICON_TRACKING)) { 380 model_type_selection |= FAVICON_TRACKING; 381 } 382 if (types.Has(syncer::DEVICE_INFO)) { 383 model_type_selection |= DEVICE_INFO; 384 } 385 if (types.Has(syncer::NIGORI)) { 386 model_type_selection |= NIGORI; 387 } 388 if (types.Has(syncer::EXPERIMENTS)) { 389 model_type_selection |= EXPERIMENTS; 390 } 391 return model_type_selection; 392 } 393 394 void ProfileSyncServiceAndroid::SetPreferredDataTypes( 395 JNIEnv* env, jobject obj, 396 jboolean sync_everything, 397 jlong model_type_selection) { 398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 399 syncer::ModelTypeSet types; 400 // Note: only user selectable types should be included here. 401 if (model_type_selection & AUTOFILL) 402 types.Put(syncer::AUTOFILL); 403 if (model_type_selection & BOOKMARK) 404 types.Put(syncer::BOOKMARKS); 405 if (model_type_selection & PASSWORD) 406 types.Put(syncer::PASSWORDS); 407 if (model_type_selection & PROXY_TABS) 408 types.Put(syncer::PROXY_TABS); 409 if (model_type_selection & TYPED_URL) 410 types.Put(syncer::TYPED_URLS); 411 DCHECK(syncer::UserSelectableTypes().HasAll(types)); 412 sync_service_->OnUserChoseDatatypes(sync_everything, types); 413 } 414 415 void ProfileSyncServiceAndroid::SetSetupInProgress( 416 JNIEnv* env, jobject obj, jboolean in_progress) { 417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 418 sync_service_->SetSetupInProgress(in_progress); 419 } 420 421 void ProfileSyncServiceAndroid::SetSyncSetupCompleted( 422 JNIEnv* env, jobject obj) { 423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 424 sync_service_->SetSyncSetupCompleted(); 425 } 426 427 jboolean ProfileSyncServiceAndroid::HasSyncSetupCompleted( 428 JNIEnv* env, jobject obj) { 429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 430 return sync_service_->HasSyncSetupCompleted(); 431 } 432 433 jboolean ProfileSyncServiceAndroid::IsStartSuppressed( 434 JNIEnv* env, jobject obj) { 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 436 return sync_prefs_->IsStartSuppressed(); 437 } 438 439 void ProfileSyncServiceAndroid::EnableEncryptEverything( 440 JNIEnv* env, jobject obj) { 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 442 sync_service_->EnableEncryptEverything(); 443 } 444 445 jboolean ProfileSyncServiceAndroid::HasKeepEverythingSynced( 446 JNIEnv* env, jobject) { 447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 448 return sync_prefs_->HasKeepEverythingSynced(); 449 } 450 451 jboolean ProfileSyncServiceAndroid::HasUnrecoverableError( 452 JNIEnv* env, jobject) { 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 454 return sync_service_->HasUnrecoverableError(); 455 } 456 457 ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetAboutInfoForTest( 458 JNIEnv* env, jobject) { 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 460 461 scoped_ptr<base::DictionaryValue> about_info = 462 sync_ui_util::ConstructAboutInformation(sync_service_); 463 std::string about_info_json; 464 base::JSONWriter::Write(about_info.get(), &about_info_json); 465 466 return ConvertUTF8ToJavaString(env, about_info_json); 467 } 468 469 jlong ProfileSyncServiceAndroid::GetLastSyncedTimeForTest( 470 JNIEnv* env, jobject obj) { 471 // Use profile preferences here instead of SyncPrefs to avoid an extra 472 // conversion, since SyncPrefs::GetLastSyncedTime() converts the stored value 473 // to to base::Time. 474 return static_cast<jlong>( 475 profile_->GetPrefs()->GetInt64(sync_driver::prefs::kSyncLastSyncedTime)); 476 } 477 478 void ProfileSyncServiceAndroid::NudgeSyncer(JNIEnv* env, 479 jobject obj, 480 jint objectSource, 481 jstring objectId, 482 jlong version, 483 jstring state) { 484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 485 SendNudgeNotification(objectSource, ConvertJavaStringToUTF8(env, objectId), 486 version, ConvertJavaStringToUTF8(env, state)); 487 } 488 489 void ProfileSyncServiceAndroid::NudgeSyncerForAllTypes(JNIEnv* env, 490 jobject obj) { 491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 492 syncer::ObjectIdInvalidationMap object_ids_with_states; 493 content::NotificationService::current()->Notify( 494 chrome::NOTIFICATION_SYNC_REFRESH_REMOTE, 495 content::Source<Profile>(profile_), 496 content::Details<const syncer::ObjectIdInvalidationMap>( 497 &object_ids_with_states)); 498 } 499 500 // static 501 ProfileSyncServiceAndroid* 502 ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid() { 503 return reinterpret_cast<ProfileSyncServiceAndroid*>( 504 Java_ProfileSyncService_getProfileSyncServiceAndroid( 505 AttachCurrentThread(), base::android::GetApplicationContext())); 506 } 507 508 static jlong Init(JNIEnv* env, jobject obj) { 509 ProfileSyncServiceAndroid* profile_sync_service_android = 510 new ProfileSyncServiceAndroid(env, obj); 511 profile_sync_service_android->Init(); 512 return reinterpret_cast<intptr_t>(profile_sync_service_android); 513 } 514 515 // static 516 bool ProfileSyncServiceAndroid::Register(JNIEnv* env) { 517 return RegisterNativesImpl(env); 518 } 519