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.
5 #include "chrome/browser/sync/profile_sync_service_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.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 "ui/base/l10n/l10n_util.h"
38 using base::android::AttachCurrentThread;
39 using base::android::CheckException;
40 using base::android::ConvertJavaStringToUTF8;
41 using base::android::ConvertUTF8ToJavaString;
42 using base::android::ScopedJavaLocalRef;
43 using content::BrowserThread;
48 #define DEFINE_MODEL_TYPE_SELECTION(name,value) name = value,
49 #include "chrome/browser/sync/profile_sync_service_model_type_selection_android.h"
50 #undef DEFINE_MODEL_TYPE_SELECTION
55 ProfileSyncServiceAndroid::ProfileSyncServiceAndroid(JNIEnv* env, jobject obj)
58 weak_java_profile_sync_service_(env, obj) {
59 if (g_browser_process == NULL ||
60 g_browser_process->profile_manager() == NULL) {
61 NOTREACHED() << "Browser process or profile manager not initialized";
65 profile_ = g_browser_process->profile_manager()->GetDefaultProfile();
66 if (profile_ == NULL) {
67 NOTREACHED() << "Sync Init: Profile not found.";
71 sync_prefs_.reset(new browser_sync::SyncPrefs(profile_->GetPrefs()));
74 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_);
75 DCHECK(sync_service_);
78 void ProfileSyncServiceAndroid::Init() {
79 sync_service_->AddObserver(this);
82 void ProfileSyncServiceAndroid::RemoveObserver() {
83 if (sync_service_->HasObserver(this)) {
84 sync_service_->RemoveObserver(this);
88 ProfileSyncServiceAndroid::~ProfileSyncServiceAndroid() {
92 void ProfileSyncServiceAndroid::SendNudgeNotification(
94 const std::string& str_object_id,
96 const std::string& state) {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
99 // TODO(nileshagrawal): Merge this with ChromeInvalidationClient::Invalidate.
100 // Construct the ModelTypeStateMap and send it over with the notification.
101 invalidation::ObjectId object_id(
104 syncer::ObjectIdInvalidationMap object_ids_with_states;
105 if (version == ipc::invalidation::Constants::UNKNOWN) {
106 object_ids_with_states.Insert(
107 syncer::Invalidation::InitUnknownVersion(object_id));
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;
116 max_invalidation_versions_[object_id] = version;
117 object_ids_with_states.Insert(
118 syncer::Invalidation::Init(object_id, version, state));
121 content::NotificationService::current()->Notify(
122 chrome::NOTIFICATION_SYNC_REFRESH_REMOTE,
123 content::Source<Profile>(profile_),
124 content::Details<const syncer::ObjectIdInvalidationMap>(
125 &object_ids_with_states));
128 void ProfileSyncServiceAndroid::OnStateChanged() {
129 // Notify the java world that our sync state has changed.
130 JNIEnv* env = AttachCurrentThread();
131 Java_ProfileSyncService_syncStateChanged(
132 env, weak_java_profile_sync_service_.get(env).obj());
135 void ProfileSyncServiceAndroid::EnableSync(JNIEnv* env, jobject) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137 // Don't need to do anything if we're already enabled.
138 if (sync_prefs_->IsStartSuppressed())
139 sync_service_->UnsuppressAndStart();
141 DVLOG(2) << "Ignoring call to EnableSync() because sync is already enabled";
144 void ProfileSyncServiceAndroid::DisableSync(JNIEnv* env, jobject) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
146 // Don't need to do anything if we're already disabled.
147 if (!sync_prefs_->IsStartSuppressed()) {
148 sync_service_->StopAndSuppress();
151 << "Ignoring call to DisableSync() because sync is already disabled";
155 void ProfileSyncServiceAndroid::SignInSync(JNIEnv* env, jobject) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 // Just return if sync already has everything it needs to start up (sync
158 // should start up automatically as long as it has credentials). This can
159 // happen normally if (for example) the user closes and reopens the sync
160 // settings window quickly during initial startup.
161 if (sync_service_->IsSyncEnabledAndLoggedIn() &&
162 sync_service_->IsOAuthRefreshTokenAvailable() &&
163 sync_service_->HasSyncSetupCompleted()) {
167 // Enable sync (if we don't have credentials yet, this will enable sync but
168 // will not start it up - sync will start once credentials arrive).
169 sync_service_->UnsuppressAndStart();
172 void ProfileSyncServiceAndroid::SignOutSync(JNIEnv* env, jobject) {
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175 sync_service_->DisableForUser();
177 // Need to clear suppress start flag manually
178 sync_prefs_->SetStartSuppressed(false);
181 ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::QuerySyncStatusSummary(
182 JNIEnv* env, jobject) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 std::string status(sync_service_->QuerySyncStatusSummaryString());
186 return ConvertUTF8ToJavaString(env, status);
189 jboolean ProfileSyncServiceAndroid::SetSyncSessionsId(
190 JNIEnv* env, jobject obj, jstring tag) {
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 std::string machine_tag = ConvertJavaStringToUTF8(env, tag);
194 sync_prefs_->SetSyncSessionsGUID(machine_tag);
198 jint ProfileSyncServiceAndroid::GetAuthError(JNIEnv* env, jobject) {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200 return sync_service_->GetAuthError().state();
203 jboolean ProfileSyncServiceAndroid::IsEncryptEverythingEnabled(
204 JNIEnv* env, jobject) {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206 return sync_service_->EncryptEverythingEnabled();
209 jboolean ProfileSyncServiceAndroid::IsSyncInitialized(JNIEnv* env, jobject) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 return sync_service_->sync_initialized();
214 jboolean ProfileSyncServiceAndroid::IsFirstSetupInProgress(
215 JNIEnv* env, jobject) {
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
217 return sync_service_->FirstSetupInProgress();
220 jboolean ProfileSyncServiceAndroid::IsPassphraseRequired(JNIEnv* env, jobject) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 return sync_service_->IsPassphraseRequired();
225 jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForDecryption(
226 JNIEnv* env, jobject obj) {
227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228 // In case of CUSTOM_PASSPHRASE we always sync passwords. Prompt the user for
229 // a passphrase if cryptographer has any pending keys.
230 if (sync_service_->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE) {
231 return !IsCryptographerReady(env, obj);
233 if (sync_service_->IsPassphraseRequiredForDecryption()) {
234 // Passwords datatype should never prompt for a passphrase, except when
235 // user is using a custom passphrase. Do not prompt for a passphrase if
236 // passwords are the only encrypted datatype. This prevents a temporary
237 // notification for passphrase when PSS has not completed configuring
238 // DataTypeManager, after configuration password datatype shall be disabled.
239 const syncer::ModelTypeSet encrypted_types =
240 sync_service_->GetEncryptedDataTypes();
241 return !encrypted_types.Equals(syncer::ModelTypeSet(syncer::PASSWORDS));
246 jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForExternalType(
247 JNIEnv* env, jobject) {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250 sync_service_->passphrase_required_reason() == syncer::REASON_DECRYPTION;
253 jboolean ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase(
254 JNIEnv* env, jobject) {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 return sync_service_->IsUsingSecondaryPassphrase();
259 jboolean ProfileSyncServiceAndroid::SetDecryptionPassphrase(
260 JNIEnv* env, jobject obj, jstring passphrase) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 std::string key = ConvertJavaStringToUTF8(env, passphrase);
263 return sync_service_->SetDecryptionPassphrase(key);
266 void ProfileSyncServiceAndroid::SetEncryptionPassphrase(
267 JNIEnv* env, jobject obj, jstring passphrase, jboolean is_gaia) {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269 std::string key = ConvertJavaStringToUTF8(env, passphrase);
270 sync_service_->SetEncryptionPassphrase(
272 is_gaia ? ProfileSyncService::IMPLICIT : ProfileSyncService::EXPLICIT);
275 jboolean ProfileSyncServiceAndroid::IsCryptographerReady(JNIEnv* env, jobject) {
276 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
277 return sync_service_->IsCryptographerReady(&trans);
280 jint ProfileSyncServiceAndroid::GetPassphraseType(JNIEnv* env, jobject) {
281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
282 return sync_service_->GetPassphraseType();
285 jboolean ProfileSyncServiceAndroid::HasExplicitPassphraseTime(
286 JNIEnv* env, jobject) {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime();
289 return !passphrase_time.is_null();
292 ScopedJavaLocalRef<jstring>
293 ProfileSyncServiceAndroid::GetSyncEnterGooglePassphraseBodyWithDateText(
294 JNIEnv* env, jobject) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime();
297 string16 passphrase_time_str = base::TimeFormatShortDate(passphrase_time);
298 return base::android::ConvertUTF16ToJavaString(env,
299 l10n_util::GetStringFUTF16(
300 IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE,
301 passphrase_time_str));
304 ScopedJavaLocalRef<jstring>
305 ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyWithDateText(
306 JNIEnv* env, jobject) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308 base::Time passphrase_time = sync_service_->GetExplicitPassphraseTime();
309 string16 passphrase_time_str = base::TimeFormatShortDate(passphrase_time);
310 return base::android::ConvertUTF16ToJavaString(env,
311 l10n_util::GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
312 passphrase_time_str));
315 ScopedJavaLocalRef<jstring>
316 ProfileSyncServiceAndroid::GetCurrentSignedInAccountText(
317 JNIEnv* env, jobject) {
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319 const std::string& sync_username =
320 SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
321 return base::android::ConvertUTF16ToJavaString(env,
322 l10n_util::GetStringFUTF16(
323 IDS_SYNC_ACCOUNT_SYNCING_TO_USER,
324 ASCIIToUTF16(sync_username)));
327 ScopedJavaLocalRef<jstring>
328 ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyText(
329 JNIEnv* env, jobject) {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 return ConvertUTF8ToJavaString(
332 env, l10n_util::GetStringUTF8(IDS_SYNC_ENTER_PASSPHRASE_BODY));
335 jboolean ProfileSyncServiceAndroid::IsSyncKeystoreMigrationDone(
336 JNIEnv* env, jobject) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338 syncer::SyncStatus status;
339 bool is_status_valid = sync_service_->QueryDetailedSyncStatus(&status);
340 return is_status_valid && !status.keystore_migration_time.is_null();
343 jlong ProfileSyncServiceAndroid::GetEnabledDataTypes(JNIEnv* env,
345 jlong model_type_selection = 0;
346 syncer::ModelTypeSet types = sync_service_->GetActiveDataTypes();
347 types.PutAll(syncer::ControlTypes());
348 if (types.Has(syncer::BOOKMARKS)) {
349 model_type_selection |= BOOKMARK;
351 if (types.Has(syncer::AUTOFILL)) {
352 model_type_selection |= AUTOFILL;
354 if (types.Has(syncer::AUTOFILL_PROFILE)) {
355 model_type_selection |= AUTOFILL_PROFILE;
357 if (types.Has(syncer::PASSWORDS)) {
358 model_type_selection |= PASSWORD;
360 if (types.Has(syncer::TYPED_URLS)) {
361 model_type_selection |= TYPED_URL;
363 if (types.Has(syncer::SESSIONS)) {
364 model_type_selection |= SESSION;
366 if (types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
367 model_type_selection |= HISTORY_DELETE_DIRECTIVE;
369 if (types.Has(syncer::PROXY_TABS)) {
370 model_type_selection |= PROXY_TABS;
372 if (types.Has(syncer::FAVICON_IMAGES)) {
373 model_type_selection |= FAVICON_IMAGE;
375 if (types.Has(syncer::FAVICON_TRACKING)) {
376 model_type_selection |= FAVICON_TRACKING;
378 if (types.Has(syncer::DEVICE_INFO)) {
379 model_type_selection |= DEVICE_INFO;
381 if (types.Has(syncer::NIGORI)) {
382 model_type_selection |= NIGORI;
384 if (types.Has(syncer::EXPERIMENTS)) {
385 model_type_selection |= EXPERIMENTS;
387 return model_type_selection;
390 void ProfileSyncServiceAndroid::SetPreferredDataTypes(
391 JNIEnv* env, jobject obj,
392 jboolean sync_everything,
393 jlong model_type_selection) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 syncer::ModelTypeSet types;
396 // Note: only user selectable types should be included here.
397 if (model_type_selection & AUTOFILL)
398 types.Put(syncer::AUTOFILL);
399 if (model_type_selection & BOOKMARK)
400 types.Put(syncer::BOOKMARKS);
401 if (model_type_selection & PASSWORD)
402 types.Put(syncer::PASSWORDS);
403 if (model_type_selection & PROXY_TABS)
404 types.Put(syncer::PROXY_TABS);
405 if (model_type_selection & TYPED_URL)
406 types.Put(syncer::TYPED_URLS);
407 DCHECK(syncer::UserSelectableTypes().HasAll(types));
408 sync_service_->OnUserChoseDatatypes(sync_everything, types);
411 void ProfileSyncServiceAndroid::SetSetupInProgress(
412 JNIEnv* env, jobject obj, jboolean in_progress) {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414 sync_service_->SetSetupInProgress(in_progress);
417 void ProfileSyncServiceAndroid::SetSyncSetupCompleted(
418 JNIEnv* env, jobject obj) {
419 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420 sync_service_->SetSyncSetupCompleted();
423 jboolean ProfileSyncServiceAndroid::HasSyncSetupCompleted(
424 JNIEnv* env, jobject obj) {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426 return sync_service_->HasSyncSetupCompleted();
429 jboolean ProfileSyncServiceAndroid::IsStartSuppressed(
430 JNIEnv* env, jobject obj) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432 return sync_prefs_->IsStartSuppressed();
435 void ProfileSyncServiceAndroid::EnableEncryptEverything(
436 JNIEnv* env, jobject obj) {
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438 sync_service_->EnableEncryptEverything();
441 jboolean ProfileSyncServiceAndroid::HasKeepEverythingSynced(
442 JNIEnv* env, jobject) {
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
444 return sync_prefs_->HasKeepEverythingSynced();
447 jboolean ProfileSyncServiceAndroid::HasUnrecoverableError(
448 JNIEnv* env, jobject) {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 return sync_service_->HasUnrecoverableError();
453 ScopedJavaLocalRef<jstring> ProfileSyncServiceAndroid::GetAboutInfoForTest(
454 JNIEnv* env, jobject) {
455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
457 scoped_ptr<DictionaryValue> about_info =
458 sync_ui_util::ConstructAboutInformation(sync_service_);
459 std::string about_info_json;
460 base::JSONWriter::Write(about_info.get(), &about_info_json);
462 return ConvertUTF8ToJavaString(env, about_info_json);
465 jlong ProfileSyncServiceAndroid::GetLastSyncedTimeForTest(
466 JNIEnv* env, jobject obj) {
467 // Use profile preferences here instead of SyncPrefs to avoid an extra
468 // conversion, since SyncPrefs::GetLastSyncedTime() converts the stored value
470 return static_cast<jlong>(
471 profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime));
474 void ProfileSyncServiceAndroid::NudgeSyncer(JNIEnv* env,
480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481 SendNudgeNotification(objectSource, ConvertJavaStringToUTF8(env, objectId),
482 version, ConvertJavaStringToUTF8(env, state));
485 void ProfileSyncServiceAndroid::NudgeSyncerForAllTypes(JNIEnv* env,
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));
497 ProfileSyncServiceAndroid*
498 ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid() {
499 return reinterpret_cast<ProfileSyncServiceAndroid*>(
500 Java_ProfileSyncService_getProfileSyncServiceAndroid(
501 AttachCurrentThread(), base::android::GetApplicationContext()));
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);
512 bool ProfileSyncServiceAndroid::Register(JNIEnv* env) {
513 return RegisterNativesImpl(env);