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