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 "sync/notifier/object_id_invalidation_map.h"
37 #include "ui/base/l10n/l10n_util.h"
39 using base::android::AttachCurrentThread
;
40 using base::android::CheckException
;
41 using base::android::ConvertJavaStringToUTF8
;
42 using base::android::ConvertUTF8ToJavaString
;
43 using base::android::ScopedJavaLocalRef
;
44 using content::BrowserThread
;
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
56 ProfileSyncServiceAndroid::ProfileSyncServiceAndroid(JNIEnv
* env
, jobject obj
)
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";
66 profile_
= ProfileManager::GetActiveUserProfile();
67 if (profile_
== NULL
) {
68 NOTREACHED() << "Sync Init: Profile not found.";
72 sync_prefs_
.reset(new browser_sync::SyncPrefs(profile_
->GetPrefs()));
75 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_
);
76 DCHECK(sync_service_
);
79 void ProfileSyncServiceAndroid::Init() {
80 sync_service_
->AddObserver(this);
83 void ProfileSyncServiceAndroid::RemoveObserver() {
84 if (sync_service_
->HasObserver(this)) {
85 sync_service_
->RemoveObserver(this);
89 ProfileSyncServiceAndroid::~ProfileSyncServiceAndroid() {
93 void ProfileSyncServiceAndroid::SendNudgeNotification(
95 const std::string
& str_object_id
,
97 const std::string
& state
) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
100 // TODO(nileshagrawal): Merge this with ChromeInvalidationClient::Invalidate.
101 // Construct the ModelTypeStateMap and send it over with the notification.
102 invalidation::ObjectId
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
));
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
;
117 max_invalidation_versions_
[object_id
] = version
;
118 object_ids_with_states
.Insert(
119 syncer::Invalidation::Init(object_id
, version
, state
));
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
));
129 void 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());
136 void 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();
142 DVLOG(2) << "Ignoring call to EnableSync() because sync is already enabled";
145 void 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();
152 << "Ignoring call to DisableSync() because sync is already disabled";
156 void 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()) {
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();
173 void ProfileSyncServiceAndroid::SignOutSync(JNIEnv
* env
, jobject
) {
174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
176 sync_service_
->DisableForUser();
178 // Need to clear suppress start flag manually
179 sync_prefs_
->SetStartSuppressed(false);
182 ScopedJavaLocalRef
<jstring
> ProfileSyncServiceAndroid::QuerySyncStatusSummary(
183 JNIEnv
* env
, jobject
) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
186 std::string
status(sync_service_
->QuerySyncStatusSummaryString());
187 return ConvertUTF8ToJavaString(env
, status
);
190 jboolean
ProfileSyncServiceAndroid::SetSyncSessionsId(
191 JNIEnv
* env
, jobject obj
, jstring tag
) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
194 std::string machine_tag
= ConvertJavaStringToUTF8(env
, tag
);
195 sync_prefs_
->SetSyncSessionsGUID(machine_tag
);
199 jint
ProfileSyncServiceAndroid::GetAuthError(JNIEnv
* env
, jobject
) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
201 return sync_service_
->GetAuthError().state();
204 jboolean
ProfileSyncServiceAndroid::IsEncryptEverythingEnabled(
205 JNIEnv
* env
, jobject
) {
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
207 return sync_service_
->EncryptEverythingEnabled();
210 jboolean
ProfileSyncServiceAndroid::IsSyncInitialized(JNIEnv
* env
, jobject
) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
212 return sync_service_
->sync_initialized();
215 jboolean
ProfileSyncServiceAndroid::IsFirstSetupInProgress(
216 JNIEnv
* env
, jobject
) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
218 return sync_service_
->FirstSetupInProgress();
221 jboolean
ProfileSyncServiceAndroid::IsPassphraseRequired(JNIEnv
* env
, jobject
) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
223 return sync_service_
->IsPassphraseRequired();
226 jboolean
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
);
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
));
247 jboolean
ProfileSyncServiceAndroid::IsPassphraseRequiredForExternalType(
248 JNIEnv
* env
, jobject
) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
251 sync_service_
->passphrase_required_reason() == syncer::REASON_DECRYPTION
;
254 jboolean
ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase(
255 JNIEnv
* env
, jobject
) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
257 return sync_service_
->IsUsingSecondaryPassphrase();
260 jboolean
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
);
267 void 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(
273 is_gaia
? ProfileSyncService::IMPLICIT
: ProfileSyncService::EXPLICIT
);
276 jboolean
ProfileSyncServiceAndroid::IsCryptographerReady(JNIEnv
* env
, jobject
) {
277 syncer::ReadTransaction
trans(FROM_HERE
, sync_service_
->GetUserShare());
278 return sync_service_
->IsCryptographerReady(&trans
);
281 jint
ProfileSyncServiceAndroid::GetPassphraseType(JNIEnv
* env
, jobject
) {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
283 return sync_service_
->GetPassphraseType();
286 jboolean
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();
293 ScopedJavaLocalRef
<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
));
306 ScopedJavaLocalRef
<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
));
318 ScopedJavaLocalRef
<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 base::ASCIIToUTF16(sync_username
)));
330 ScopedJavaLocalRef
<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
));
338 jboolean
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();
346 jlong
ProfileSyncServiceAndroid::GetEnabledDataTypes(JNIEnv
* env
,
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
;
354 if (types
.Has(syncer::AUTOFILL
)) {
355 model_type_selection
|= AUTOFILL
;
357 if (types
.Has(syncer::AUTOFILL_PROFILE
)) {
358 model_type_selection
|= AUTOFILL_PROFILE
;
360 if (types
.Has(syncer::PASSWORDS
)) {
361 model_type_selection
|= PASSWORD
;
363 if (types
.Has(syncer::TYPED_URLS
)) {
364 model_type_selection
|= TYPED_URL
;
366 if (types
.Has(syncer::SESSIONS
)) {
367 model_type_selection
|= SESSION
;
369 if (types
.Has(syncer::HISTORY_DELETE_DIRECTIVES
)) {
370 model_type_selection
|= HISTORY_DELETE_DIRECTIVE
;
372 if (types
.Has(syncer::PROXY_TABS
)) {
373 model_type_selection
|= PROXY_TABS
;
375 if (types
.Has(syncer::FAVICON_IMAGES
)) {
376 model_type_selection
|= FAVICON_IMAGE
;
378 if (types
.Has(syncer::FAVICON_TRACKING
)) {
379 model_type_selection
|= FAVICON_TRACKING
;
381 if (types
.Has(syncer::DEVICE_INFO
)) {
382 model_type_selection
|= DEVICE_INFO
;
384 if (types
.Has(syncer::NIGORI
)) {
385 model_type_selection
|= NIGORI
;
387 if (types
.Has(syncer::EXPERIMENTS
)) {
388 model_type_selection
|= EXPERIMENTS
;
390 return model_type_selection
;
393 void 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
);
414 void ProfileSyncServiceAndroid::SetSetupInProgress(
415 JNIEnv
* env
, jobject obj
, jboolean in_progress
) {
416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
417 sync_service_
->SetSetupInProgress(in_progress
);
420 void ProfileSyncServiceAndroid::SetSyncSetupCompleted(
421 JNIEnv
* env
, jobject obj
) {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
423 sync_service_
->SetSyncSetupCompleted();
426 jboolean
ProfileSyncServiceAndroid::HasSyncSetupCompleted(
427 JNIEnv
* env
, jobject obj
) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
429 return sync_service_
->HasSyncSetupCompleted();
432 jboolean
ProfileSyncServiceAndroid::IsStartSuppressed(
433 JNIEnv
* env
, jobject obj
) {
434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
435 return sync_prefs_
->IsStartSuppressed();
438 void ProfileSyncServiceAndroid::EnableEncryptEverything(
439 JNIEnv
* env
, jobject obj
) {
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
441 sync_service_
->EnableEncryptEverything();
444 jboolean
ProfileSyncServiceAndroid::HasKeepEverythingSynced(
445 JNIEnv
* env
, jobject
) {
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
447 return sync_prefs_
->HasKeepEverythingSynced();
450 jboolean
ProfileSyncServiceAndroid::HasUnrecoverableError(
451 JNIEnv
* env
, jobject
) {
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
453 return sync_service_
->HasUnrecoverableError();
456 ScopedJavaLocalRef
<jstring
> ProfileSyncServiceAndroid::GetAboutInfoForTest(
457 JNIEnv
* env
, jobject
) {
458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
460 scoped_ptr
<base::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
);
465 return ConvertUTF8ToJavaString(env
, about_info_json
);
468 jlong
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
473 return static_cast<jlong
>(
474 profile_
->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime
));
477 void ProfileSyncServiceAndroid::NudgeSyncer(JNIEnv
* env
,
483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
484 SendNudgeNotification(objectSource
, ConvertJavaStringToUTF8(env
, objectId
),
485 version
, ConvertJavaStringToUTF8(env
, state
));
488 void ProfileSyncServiceAndroid::NudgeSyncerForAllTypes(JNIEnv
* env
,
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
));
500 ProfileSyncServiceAndroid
*
501 ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid() {
502 return reinterpret_cast<ProfileSyncServiceAndroid
*>(
503 Java_ProfileSyncService_getProfileSyncServiceAndroid(
504 AttachCurrentThread(), base::android::GetApplicationContext()));
507 static 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
);
515 bool ProfileSyncServiceAndroid::Register(JNIEnv
* env
) {
516 return RegisterNativesImpl(env
);