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