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 package org.chromium.chrome.browser.sync;
7 import android.content.Context;
8 import android.util.Log;
10 import org.chromium.base.CalledByNative;
11 import org.chromium.base.ThreadUtils;
12 import org.chromium.base.VisibleForTesting;
13 import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator;
14 import org.chromium.sync.internal_api.pub.SyncDecryptionPassphraseType;
15 import org.chromium.sync.internal_api.pub.base.ModelType;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.SortedSet;
22 import java.util.TreeSet;
23 import java.util.concurrent.CopyOnWriteArrayList;
26 * Android wrapper of the ProfileSyncService which provides access from the Java layer.
28 * This class mostly wraps native classes, but it make a few business logic decisions, both in Java
31 * Only usable from the UI thread as the native ProfileSyncService requires its access to be in the
34 * See chrome/browser/sync/profile_sync_service.h for more details.
36 public class ProfileSyncService {
38 public interface SyncStateChangedListener {
39 // Invoked when the underlying sync status has changed.
40 public void syncStateChanged();
43 private static final String TAG = "ProfileSyncService";
46 public static final String SESSION_TAG_PREFIX = "session_sync";
48 private static ProfileSyncService sSyncSetupManager;
51 protected final Context mContext;
53 // Sync state changes more often than listeners are added/removed, so using CopyOnWrite.
54 private final List<SyncStateChangedListener> mListeners =
55 new CopyOnWriteArrayList<SyncStateChangedListener>();
57 // Native ProfileSyncServiceAndroid object. Can not be final since we set it to 0 in destroy().
58 private final long mNativeProfileSyncServiceAndroid;
61 * A helper method for retrieving the application-wide SyncSetupManager.
63 * Can only be accessed on the main thread.
65 * @param context the ApplicationContext is retrieved from the context used as an argument.
66 * @return a singleton instance of the SyncSetupManager
68 public static ProfileSyncService get(Context context) {
69 ThreadUtils.assertOnUiThread();
70 if (sSyncSetupManager == null) {
71 sSyncSetupManager = new ProfileSyncService(context);
73 return sSyncSetupManager;
77 * This is called pretty early in our application. Avoid any blocking operations here.
79 private ProfileSyncService(Context context) {
80 ThreadUtils.assertOnUiThread();
81 // We should store the application context, as we outlive any activity which may create us.
82 mContext = context.getApplicationContext();
84 // This may cause us to create ProfileSyncService even if sync has not
85 // been set up, but ProfileSyncService::Startup() won't be called until
86 // credentials are available.
87 mNativeProfileSyncServiceAndroid = nativeInit();
91 private static long getProfileSyncServiceAndroid(Context context) {
92 return get(context).mNativeProfileSyncServiceAndroid;
96 * If we are currently in the process of setting up sync, this method clears the
97 * sync setup in progress flag.
100 public void finishSyncFirstSetupIfNeeded() {
101 if (isFirstSetupInProgress()) {
102 setSyncSetupCompleted();
103 setSetupInProgress(false);
107 public void signOut() {
108 nativeSignOutSync(mNativeProfileSyncServiceAndroid);
112 * Signs in to sync, using the currently signed-in account.
114 public void syncSignIn() {
115 nativeSignInSync(mNativeProfileSyncServiceAndroid);
116 // Notify listeners right away that the sync state has changed (native side does not do
122 * Signs in to sync, using the existing auth token.
125 public void syncSignIn(String account) {
132 * @param account The username of the account that is signing in.
133 * @param authToken Not used. ProfileSyncService switched to OAuth2 tokens.
134 * Deprecated. Use syncSignIn instead.
137 public void syncSignInWithAuthToken(String account, String authToken) {
141 public void requestSyncFromNativeChrome(
142 int objectSource, String objectId, long version, String payload) {
143 ThreadUtils.assertOnUiThread();
145 mNativeProfileSyncServiceAndroid, objectSource, objectId, version, payload);
148 public void requestSyncFromNativeChromeForAllTypes() {
149 ThreadUtils.assertOnUiThread();
150 nativeNudgeSyncerForAllTypes(mNativeProfileSyncServiceAndroid);
154 * Nudge the syncer to start a new sync cycle.
157 public void requestSyncCycleForTest() {
158 ThreadUtils.assertOnUiThread();
159 requestSyncFromNativeChromeForAllTypes();
162 public String querySyncStatus() {
163 ThreadUtils.assertOnUiThread();
164 return nativeQuerySyncStatusSummary(mNativeProfileSyncServiceAndroid);
168 * Sets the the machine tag used by session sync to a unique value.
170 public void setSessionsId(UniqueIdentificationGenerator generator) {
171 ThreadUtils.assertOnUiThread();
172 String uniqueTag = generator.getUniqueId(null);
173 if (uniqueTag.isEmpty()) {
174 Log.e(TAG, "Unable to get unique tag for sync. " +
175 "This may lead to unexpected tab sync behavior.");
178 String sessionTag = SESSION_TAG_PREFIX + uniqueTag;
179 if (!nativeSetSyncSessionsId(mNativeProfileSyncServiceAndroid, sessionTag)) {
180 Log.e(TAG, "Unable to write session sync tag. " +
181 "This may lead to unexpected tab sync behavior.");
186 * Checks if a password or a passphrase is required for decryption of sync data.
188 * Returns NONE if the state is unavailable, or decryption passphrase/password is not required.
190 * @return the enum describing the decryption passphrase type required
192 public SyncDecryptionPassphraseType getSyncDecryptionPassphraseTypeIfRequired() {
193 // ProfileSyncService::IsUsingSecondaryPassphrase() requires the sync backend to be
194 // initialized, and that happens just after OnPassphraseRequired(). Therefore, we need to
195 // guard that call with a check of the sync backend since we can not be sure which
196 // passphrase type we should tell the user we need.
197 // This is tracked in:
198 // http://code.google.com/p/chromium/issues/detail?id=108127
199 if (isSyncInitialized() && isPassphraseRequiredForDecryption()) {
200 return getSyncDecryptionPassphraseType();
202 return SyncDecryptionPassphraseType.NONE;
206 * Returns the actual passphrase type being used for encryption. The sync backend must be
207 * running (isSyncInitialized() returns true) before calling this function.
209 * This method should only be used if you want to know the raw value. For checking whether we
210 * should ask the user for a passphrase, you should instead use
211 * getSyncDecryptionPassphraseTypeIfRequired().
213 public SyncDecryptionPassphraseType getSyncDecryptionPassphraseType() {
214 assert isSyncInitialized();
215 int passphraseType = nativeGetPassphraseType(mNativeProfileSyncServiceAndroid);
216 return SyncDecryptionPassphraseType.fromInternalValue(passphraseType);
219 public boolean isSyncKeystoreMigrationDone() {
220 assert isSyncInitialized();
221 return nativeIsSyncKeystoreMigrationDone(mNativeProfileSyncServiceAndroid);
225 * Returns true if the current explicit passphrase time is defined.
227 public boolean hasExplicitPassphraseTime() {
228 assert isSyncInitialized();
229 return nativeHasExplicitPassphraseTime(mNativeProfileSyncServiceAndroid);
232 public String getSyncEnterGooglePassphraseBodyWithDateText() {
233 assert isSyncInitialized();
234 return nativeGetSyncEnterGooglePassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
237 public String getSyncEnterCustomPassphraseBodyWithDateText() {
238 assert isSyncInitialized();
239 return nativeGetSyncEnterCustomPassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
242 public String getCurrentSignedInAccountText() {
243 assert isSyncInitialized();
244 return nativeGetCurrentSignedInAccountText(mNativeProfileSyncServiceAndroid);
247 public String getSyncEnterCustomPassphraseBodyText() {
248 return nativeGetSyncEnterCustomPassphraseBodyText(mNativeProfileSyncServiceAndroid);
252 * Checks if sync is currently set to use a custom passphrase. The sync backend must be running
253 * (isSyncInitialized() returns true) before calling this function.
255 * @return true if sync is using a custom passphrase.
257 public boolean isUsingSecondaryPassphrase() {
258 assert isSyncInitialized();
259 return nativeIsUsingSecondaryPassphrase(mNativeProfileSyncServiceAndroid);
263 * Checks if we need a passphrase to decrypt a currently-enabled data type. This returns false
264 * if a passphrase is needed for a type that is not currently enabled.
266 * @return true if we need a passphrase.
268 public boolean isPassphraseRequiredForDecryption() {
269 assert isSyncInitialized();
270 return nativeIsPassphraseRequiredForDecryption(mNativeProfileSyncServiceAndroid);
274 * Checks if we need a passphrase to decrypt any data type (including types that aren't
275 * currently enabled or supported, such as passwords). This API is used to determine if we
276 * need to provide a decryption passphrase before we can re-encrypt with a custom passphrase.
278 * @return true if we need a passphrase for some type.
280 public boolean isPassphraseRequiredForExternalType() {
281 assert isSyncInitialized();
282 return nativeIsPassphraseRequiredForExternalType(mNativeProfileSyncServiceAndroid);
286 * Checks if the sync backend is running.
288 * @return true if sync is initialized/running.
290 public boolean isSyncInitialized() {
291 return nativeIsSyncInitialized(mNativeProfileSyncServiceAndroid);
295 * Checks if the first sync setup is currently in progress.
297 * @return true if first sync setup is in progress
299 public boolean isFirstSetupInProgress() {
300 return nativeIsFirstSetupInProgress(mNativeProfileSyncServiceAndroid);
304 * Checks if the all the data types are encrypted.
306 * @return true if all data types are encrypted, false if only passwords are encrypted.
308 public boolean isEncryptEverythingEnabled() {
309 assert isSyncInitialized();
310 return nativeIsEncryptEverythingEnabled(mNativeProfileSyncServiceAndroid);
314 * Turns on encryption of all data types. This only takes effect after sync configuration is
315 * completed and setPreferredDataTypes() is invoked.
317 public void enableEncryptEverything() {
318 assert isSyncInitialized();
319 nativeEnableEncryptEverything(mNativeProfileSyncServiceAndroid);
322 public void setEncryptionPassphrase(String passphrase, boolean isGaia) {
323 assert isSyncInitialized();
324 nativeSetEncryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase, isGaia);
327 public boolean isCryptographerReady() {
328 assert isSyncInitialized();
329 return nativeIsCryptographerReady(mNativeProfileSyncServiceAndroid);
332 public boolean setDecryptionPassphrase(String passphrase) {
333 assert isSyncInitialized();
334 return nativeSetDecryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase);
337 public GoogleServiceAuthError.State getAuthError() {
338 int authErrorCode = nativeGetAuthError(mNativeProfileSyncServiceAndroid);
339 return GoogleServiceAuthError.State.fromCode(authErrorCode);
343 * Gets the set of data types that are currently enabled to sync.
345 * @return Set of enabled types.
347 public Set<ModelType> getPreferredDataTypes() {
348 long modelTypeSelection =
349 nativeGetEnabledDataTypes(mNativeProfileSyncServiceAndroid);
350 return modelTypeSelectionToSet(modelTypeSelection);
354 public static Set<ModelType> modelTypeSelectionToSet(long modelTypeSelection) {
355 Set<ModelType> syncTypes = new HashSet<ModelType>();
356 if ((modelTypeSelection & ModelTypeSelection.AUTOFILL) != 0) {
357 syncTypes.add(ModelType.AUTOFILL);
359 if ((modelTypeSelection & ModelTypeSelection.AUTOFILL_PROFILE) != 0) {
360 syncTypes.add(ModelType.AUTOFILL_PROFILE);
362 if ((modelTypeSelection & ModelTypeSelection.BOOKMARK) != 0) {
363 syncTypes.add(ModelType.BOOKMARK);
365 if ((modelTypeSelection & ModelTypeSelection.EXPERIMENTS) != 0) {
366 syncTypes.add(ModelType.EXPERIMENTS);
368 if ((modelTypeSelection & ModelTypeSelection.NIGORI) != 0) {
369 syncTypes.add(ModelType.NIGORI);
371 if ((modelTypeSelection & ModelTypeSelection.PASSWORD) != 0) {
372 syncTypes.add(ModelType.PASSWORD);
374 if ((modelTypeSelection & ModelTypeSelection.SESSION) != 0) {
375 syncTypes.add(ModelType.SESSION);
377 if ((modelTypeSelection & ModelTypeSelection.TYPED_URL) != 0) {
378 syncTypes.add(ModelType.TYPED_URL);
380 if ((modelTypeSelection & ModelTypeSelection.HISTORY_DELETE_DIRECTIVE) != 0) {
381 syncTypes.add(ModelType.HISTORY_DELETE_DIRECTIVE);
383 if ((modelTypeSelection & ModelTypeSelection.DEVICE_INFO) != 0) {
384 syncTypes.add(ModelType.DEVICE_INFO);
386 if ((modelTypeSelection & ModelTypeSelection.PROXY_TABS) != 0) {
387 syncTypes.add(ModelType.PROXY_TABS);
389 if ((modelTypeSelection & ModelTypeSelection.FAVICON_IMAGE) != 0) {
390 syncTypes.add(ModelType.FAVICON_IMAGE);
392 if ((modelTypeSelection & ModelTypeSelection.FAVICON_TRACKING) != 0) {
393 syncTypes.add(ModelType.FAVICON_TRACKING);
395 if ((modelTypeSelection & ModelTypeSelection.SUPERVISED_USER_SETTING) != 0) {
396 syncTypes.add(ModelType.MANAGED_USER_SETTING);
401 public boolean hasKeepEverythingSynced() {
402 return nativeHasKeepEverythingSynced(mNativeProfileSyncServiceAndroid);
406 * Enables syncing for the passed data types.
408 * @param syncEverything Set to true if the user wants to sync all data types
409 * (including new data types we add in the future).
410 * @param enabledTypes The set of types to enable. Ignored (can be null) if
411 * syncEverything is true.
413 public void setPreferredDataTypes(boolean syncEverything, Set<ModelType> enabledTypes) {
414 long modelTypeSelection = 0;
415 if (syncEverything || enabledTypes.contains(ModelType.AUTOFILL)) {
416 modelTypeSelection |= ModelTypeSelection.AUTOFILL;
418 if (syncEverything || enabledTypes.contains(ModelType.BOOKMARK)) {
419 modelTypeSelection |= ModelTypeSelection.BOOKMARK;
421 if (syncEverything || enabledTypes.contains(ModelType.PASSWORD)) {
422 modelTypeSelection |= ModelTypeSelection.PASSWORD;
424 if (syncEverything || enabledTypes.contains(ModelType.PROXY_TABS)) {
425 modelTypeSelection |= ModelTypeSelection.PROXY_TABS;
427 if (syncEverything || enabledTypes.contains(ModelType.TYPED_URL)) {
428 modelTypeSelection |= ModelTypeSelection.TYPED_URL;
430 nativeSetPreferredDataTypes(
431 mNativeProfileSyncServiceAndroid, syncEverything, modelTypeSelection);
434 public void setSyncSetupCompleted() {
435 nativeSetSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
438 public boolean hasSyncSetupCompleted() {
439 return nativeHasSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
442 public boolean isStartSuppressed() {
443 return nativeIsStartSuppressed(mNativeProfileSyncServiceAndroid);
447 * Notifies sync whether sync setup is in progress - this tells sync whether it should start
448 * syncing data types when it starts up, or if it should just stay in "configuration mode".
450 * @param inProgress True to put sync in configuration mode, false to turn off configuration
453 public void setSetupInProgress(boolean inProgress) {
454 nativeSetSetupInProgress(mNativeProfileSyncServiceAndroid, inProgress);
457 public void addSyncStateChangedListener(SyncStateChangedListener listener) {
458 ThreadUtils.assertOnUiThread();
459 mListeners.add(listener);
462 public void removeSyncStateChangedListener(SyncStateChangedListener listener) {
463 ThreadUtils.assertOnUiThread();
464 mListeners.remove(listener);
467 public boolean hasUnrecoverableError() {
468 return nativeHasUnrecoverableError(mNativeProfileSyncServiceAndroid);
472 * Called when the state of the native sync engine has changed, so various
473 * UI elements can update themselves.
476 public void syncStateChanged() {
477 if (!mListeners.isEmpty()) {
478 for (SyncStateChangedListener listener : mListeners) {
479 listener.syncStateChanged();
485 public String getSyncInternalsInfoForTest() {
486 ThreadUtils.assertOnUiThread();
487 return nativeGetAboutInfoForTest(mNativeProfileSyncServiceAndroid);
491 * Starts the sync engine.
493 public void enableSync() {
494 nativeEnableSync(mNativeProfileSyncServiceAndroid);
498 * Stops the sync engine.
500 public void disableSync() {
501 nativeDisableSync(mNativeProfileSyncServiceAndroid);
505 * Returns the time when the last sync cycle was completed.
507 * @return The difference measured in microseconds, between last sync cycle completion time
508 * and 1 January 1970 00:00:00 UTC.
511 public long getLastSyncedTimeForTest() {
512 return nativeGetLastSyncedTimeForTest(mNativeProfileSyncServiceAndroid);
516 * Overrides the Sync engine's NetworkResources. This is used to set up the Sync FakeServer for
519 * @param networkResources the pointer to the NetworkResources created by the native code. It
520 * is assumed that the Java caller has ownership of this pointer;
521 * ownership is transferred as part of this call.
523 public void overrideNetworkResourcesForTest(long networkResources) {
524 nativeOverrideNetworkResourcesForTest(mNativeProfileSyncServiceAndroid, networkResources);
528 private static String modelTypeSelectionToStringForTest(long modelTypeSelection) {
529 SortedSet<String> set = new TreeSet<String>();
530 Set<ModelType> filteredTypes = ModelType.filterOutNonInvalidationTypes(
531 modelTypeSelectionToSet(modelTypeSelection));
532 for (ModelType type : filteredTypes) {
533 set.add(type.toString());
535 StringBuilder sb = new StringBuilder();
536 Iterator<String> it = set.iterator();
538 sb.append(it.next());
539 while (it.hasNext()) {
541 sb.append(it.next());
544 return sb.toString();
548 private native void nativeNudgeSyncer(
549 long nativeProfileSyncServiceAndroid, int objectSource, String objectId, long version,
551 private native void nativeNudgeSyncerForAllTypes(long nativeProfileSyncServiceAndroid);
552 private native long nativeInit();
553 private native void nativeEnableSync(long nativeProfileSyncServiceAndroid);
554 private native void nativeDisableSync(long nativeProfileSyncServiceAndroid);
555 private native void nativeSignInSync(long nativeProfileSyncServiceAndroid);
556 private native void nativeSignOutSync(long nativeProfileSyncServiceAndroid);
557 private native boolean nativeSetSyncSessionsId(
558 long nativeProfileSyncServiceAndroid, String tag);
559 private native String nativeQuerySyncStatusSummary(long nativeProfileSyncServiceAndroid);
560 private native int nativeGetAuthError(long nativeProfileSyncServiceAndroid);
561 private native boolean nativeIsSyncInitialized(long nativeProfileSyncServiceAndroid);
562 private native boolean nativeIsFirstSetupInProgress(long nativeProfileSyncServiceAndroid);
563 private native boolean nativeIsEncryptEverythingEnabled(long nativeProfileSyncServiceAndroid);
564 private native void nativeEnableEncryptEverything(long nativeProfileSyncServiceAndroid);
565 private native boolean nativeIsPassphraseRequiredForDecryption(
566 long nativeProfileSyncServiceAndroid);
567 private native boolean nativeIsPassphraseRequiredForExternalType(
568 long nativeProfileSyncServiceAndroid);
569 private native boolean nativeIsUsingSecondaryPassphrase(long nativeProfileSyncServiceAndroid);
570 private native boolean nativeSetDecryptionPassphrase(
571 long nativeProfileSyncServiceAndroid, String passphrase);
572 private native void nativeSetEncryptionPassphrase(
573 long nativeProfileSyncServiceAndroid, String passphrase, boolean isGaia);
574 private native boolean nativeIsCryptographerReady(long nativeProfileSyncServiceAndroid);
575 private native int nativeGetPassphraseType(long nativeProfileSyncServiceAndroid);
576 private native boolean nativeHasExplicitPassphraseTime(long nativeProfileSyncServiceAndroid);
577 private native String nativeGetSyncEnterGooglePassphraseBodyWithDateText(
578 long nativeProfileSyncServiceAndroid);
579 private native String nativeGetSyncEnterCustomPassphraseBodyWithDateText(
580 long nativeProfileSyncServiceAndroid);
581 private native String nativeGetCurrentSignedInAccountText(long nativeProfileSyncServiceAndroid);
582 private native String nativeGetSyncEnterCustomPassphraseBodyText(
583 long nativeProfileSyncServiceAndroid);
584 private native boolean nativeIsSyncKeystoreMigrationDone(long nativeProfileSyncServiceAndroid);
585 private native long nativeGetEnabledDataTypes(
586 long nativeProfileSyncServiceAndroid);
587 private native void nativeSetPreferredDataTypes(
588 long nativeProfileSyncServiceAndroid, boolean syncEverything, long modelTypeSelection);
589 private native void nativeSetSetupInProgress(
590 long nativeProfileSyncServiceAndroid, boolean inProgress);
591 private native void nativeSetSyncSetupCompleted(long nativeProfileSyncServiceAndroid);
592 private native boolean nativeHasSyncSetupCompleted(long nativeProfileSyncServiceAndroid);
593 private native boolean nativeIsStartSuppressed(long nativeProfileSyncServiceAndroid);
594 private native boolean nativeHasKeepEverythingSynced(long nativeProfileSyncServiceAndroid);
595 private native boolean nativeHasUnrecoverableError(long nativeProfileSyncServiceAndroid);
596 private native String nativeGetAboutInfoForTest(long nativeProfileSyncServiceAndroid);
597 private native long nativeGetLastSyncedTimeForTest(long nativeProfileSyncServiceAndroid);
598 private native void nativeOverrideNetworkResourcesForTest(
599 long nativeProfileSyncServiceAndroid, long networkResources);