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_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"
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
;
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
57 ProfileSyncServiceAndroid::ProfileSyncServiceAndroid(JNIEnv
* env
, jobject obj
)
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";
67 profile_
= ProfileManager::GetActiveUserProfile();
68 if (profile_
== NULL
) {
69 NOTREACHED() << "Sync Init: Profile not found.";
73 sync_prefs_
.reset(new sync_driver::SyncPrefs(profile_
->GetPrefs()));
76 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_
);
77 DCHECK(sync_service_
);
80 void ProfileSyncServiceAndroid::Init() {
81 sync_service_
->AddObserver(this);
84 void ProfileSyncServiceAndroid::RemoveObserver() {
85 if (sync_service_
->HasObserver(this)) {
86 sync_service_
->RemoveObserver(this);
90 ProfileSyncServiceAndroid::~ProfileSyncServiceAndroid() {
94 void ProfileSyncServiceAndroid::SendNudgeNotification(
96 const std::string
& str_object_id
,
98 const std::string
& state
) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
101 // TODO(nileshagrawal): Merge this with ChromeInvalidationClient::Invalidate.
102 // Construct the ModelTypeStateMap and send it over with the notification.
103 invalidation::ObjectId
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
));
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
;
118 max_invalidation_versions_
[object_id
] = version
;
119 object_ids_with_states
.Insert(
120 syncer::Invalidation::Init(object_id
, version
, state
));
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
));
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());
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();
143 DVLOG(2) << "Ignoring call to EnableSync() because sync is already enabled";
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();
153 << "Ignoring call to DisableSync() because sync is already disabled";
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()) {
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();
174 void ProfileSyncServiceAndroid::SignOutSync(JNIEnv
* env
, jobject
) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
177 sync_service_
->DisableForUser();
179 // Need to clear suppress start flag manually
180 sync_prefs_
->SetStartSuppressed(false);
183 ScopedJavaLocalRef
<jstring
> ProfileSyncServiceAndroid::QuerySyncStatusSummary(
184 JNIEnv
* env
, jobject
) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
187 std::string
status(sync_service_
->QuerySyncStatusSummaryString());
188 return ConvertUTF8ToJavaString(env
, status
);
191 jboolean
ProfileSyncServiceAndroid::SetSyncSessionsId(
192 JNIEnv
* env
, jobject obj
, jstring tag
) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
195 std::string machine_tag
= ConvertJavaStringToUTF8(env
, tag
);
196 sync_prefs_
->SetSyncSessionsGUID(machine_tag
);
200 jint
ProfileSyncServiceAndroid::GetAuthError(JNIEnv
* env
, jobject
) {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
202 return sync_service_
->GetAuthError().state();
205 jboolean
ProfileSyncServiceAndroid::IsEncryptEverythingEnabled(
206 JNIEnv
* env
, jobject
) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
208 return sync_service_
->EncryptEverythingEnabled();
211 jboolean
ProfileSyncServiceAndroid::IsSyncInitialized(JNIEnv
* env
, jobject
) {
212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
213 return sync_service_
->sync_initialized();
216 jboolean
ProfileSyncServiceAndroid::IsFirstSetupInProgress(
217 JNIEnv
* env
, jobject
) {
218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
219 return sync_service_
->FirstSetupInProgress();
222 jboolean
ProfileSyncServiceAndroid::IsPassphraseRequired(JNIEnv
* env
, jobject
) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
224 return sync_service_
->IsPassphraseRequired();
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
);
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
));
248 jboolean
ProfileSyncServiceAndroid::IsPassphraseRequiredForExternalType(
249 JNIEnv
* env
, jobject
) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
252 sync_service_
->passphrase_required_reason() == syncer::REASON_DECRYPTION
;
255 jboolean
ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase(
256 JNIEnv
* env
, jobject
) {
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
258 return sync_service_
->IsUsingSecondaryPassphrase();
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
);
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(
274 is_gaia
? ProfileSyncService::IMPLICIT
: ProfileSyncService::EXPLICIT
);
277 jboolean
ProfileSyncServiceAndroid::IsCryptographerReady(JNIEnv
* env
, jobject
) {
278 syncer::ReadTransaction
trans(FROM_HERE
, sync_service_
->GetUserShare());
279 return sync_service_
->IsCryptographerReady(&trans
);
282 jint
ProfileSyncServiceAndroid::GetPassphraseType(JNIEnv
* env
, jobject
) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
284 return sync_service_
->GetPassphraseType();
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();
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
));
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
));
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
)));
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
));
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();
347 jlong
ProfileSyncServiceAndroid::GetEnabledDataTypes(JNIEnv
* env
,
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
;
355 if (types
.Has(syncer::AUTOFILL
)) {
356 model_type_selection
|= AUTOFILL
;
358 if (types
.Has(syncer::AUTOFILL_PROFILE
)) {
359 model_type_selection
|= AUTOFILL_PROFILE
;
361 if (types
.Has(syncer::PASSWORDS
)) {
362 model_type_selection
|= PASSWORD
;
364 if (types
.Has(syncer::TYPED_URLS
)) {
365 model_type_selection
|= TYPED_URL
;
367 if (types
.Has(syncer::SESSIONS
)) {
368 model_type_selection
|= SESSION
;
370 if (types
.Has(syncer::HISTORY_DELETE_DIRECTIVES
)) {
371 model_type_selection
|= HISTORY_DELETE_DIRECTIVE
;
373 if (types
.Has(syncer::PROXY_TABS
)) {
374 model_type_selection
|= PROXY_TABS
;
376 if (types
.Has(syncer::FAVICON_IMAGES
)) {
377 model_type_selection
|= FAVICON_IMAGE
;
379 if (types
.Has(syncer::FAVICON_TRACKING
)) {
380 model_type_selection
|= FAVICON_TRACKING
;
382 if (types
.Has(syncer::DEVICE_INFO
)) {
383 model_type_selection
|= DEVICE_INFO
;
385 if (types
.Has(syncer::NIGORI
)) {
386 model_type_selection
|= NIGORI
;
388 if (types
.Has(syncer::EXPERIMENTS
)) {
389 model_type_selection
|= EXPERIMENTS
;
391 return model_type_selection
;
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
);
415 void ProfileSyncServiceAndroid::SetSetupInProgress(
416 JNIEnv
* env
, jobject obj
, jboolean in_progress
) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
418 sync_service_
->SetSetupInProgress(in_progress
);
421 void ProfileSyncServiceAndroid::SetSyncSetupCompleted(
422 JNIEnv
* env
, jobject obj
) {
423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
424 sync_service_
->SetSyncSetupCompleted();
427 jboolean
ProfileSyncServiceAndroid::HasSyncSetupCompleted(
428 JNIEnv
* env
, jobject obj
) {
429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
430 return sync_service_
->HasSyncSetupCompleted();
433 jboolean
ProfileSyncServiceAndroid::IsStartSuppressed(
434 JNIEnv
* env
, jobject obj
) {
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
436 return sync_prefs_
->IsStartSuppressed();
439 void ProfileSyncServiceAndroid::EnableEncryptEverything(
440 JNIEnv
* env
, jobject obj
) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
442 sync_service_
->EnableEncryptEverything();
445 jboolean
ProfileSyncServiceAndroid::HasKeepEverythingSynced(
446 JNIEnv
* env
, jobject
) {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
448 return sync_prefs_
->HasKeepEverythingSynced();
451 jboolean
ProfileSyncServiceAndroid::HasUnrecoverableError(
452 JNIEnv
* env
, jobject
) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
454 return sync_service_
->HasUnrecoverableError();
457 ScopedJavaLocalRef
<jstring
> ProfileSyncServiceAndroid::GetAboutInfoForTest(
458 JNIEnv
* env
, jobject
) {
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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
);
466 return ConvertUTF8ToJavaString(env
, about_info_json
);
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
474 return static_cast<jlong
>(
475 profile_
->GetPrefs()->GetInt64(sync_driver::prefs::kSyncLastSyncedTime
));
478 void ProfileSyncServiceAndroid::NudgeSyncer(JNIEnv
* env
,
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
485 SendNudgeNotification(objectSource
, ConvertJavaStringToUTF8(env
, objectId
),
486 version
, ConvertJavaStringToUTF8(env
, state
));
489 void ProfileSyncServiceAndroid::NudgeSyncerForAllTypes(JNIEnv
* env
,
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
));
501 ProfileSyncServiceAndroid
*
502 ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid() {
503 return reinterpret_cast<ProfileSyncServiceAndroid
*>(
504 Java_ProfileSyncService_getProfileSyncServiceAndroid(
505 AttachCurrentThread(), base::android::GetApplicationContext()));
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
);
516 bool ProfileSyncServiceAndroid::Register(JNIEnv
* env
) {
517 return RegisterNativesImpl(env
);