- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / sync / ProfileSyncService.java
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 package org.chromium.chrome.browser.sync;
6
7 import android.content.Context;
8 import android.util.Log;
9
10 import com.google.common.annotations.VisibleForTesting;
11
12 import org.chromium.base.CalledByNative;
13 import org.chromium.base.ThreadUtils;
14 import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator;
15 import org.chromium.sync.internal_api.pub.SyncDecryptionPassphraseType;
16 import org.chromium.sync.internal_api.pub.base.ModelType;
17
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.concurrent.CopyOnWriteArrayList;
22
23 /**
24  * Android wrapper of the ProfileSyncService which provides access from the Java layer.
25  * <p/>
26  * This class mostly wraps native classes, but it make a few business logic decisions, both in Java
27  * and in native.
28  * <p/>
29  * Only usable from the UI thread as the native ProfileSyncService requires its access to be in the
30  * UI thread.
31  * <p/>
32  * See chrome/browser/sync/profile_sync_service.h for more details.
33  */
34 public class ProfileSyncService {
35
36     public interface SyncStateChangedListener {
37         // Invoked when the underlying sync status has changed.
38         public void syncStateChanged();
39     }
40
41     private static final String TAG = "ProfileSyncService";
42
43     @VisibleForTesting
44     public static final String SESSION_TAG_PREFIX = "session_sync";
45
46     private static ProfileSyncService sSyncSetupManager;
47
48     @VisibleForTesting
49     protected final Context mContext;
50
51     // Sync state changes more often than listeners are added/removed, so using CopyOnWrite.
52     private final List<SyncStateChangedListener> mListeners =
53             new CopyOnWriteArrayList<SyncStateChangedListener>();
54
55     // Native ProfileSyncServiceAndroid object. Can not be final since we set it to 0 in destroy().
56     private final int mNativeProfileSyncServiceAndroid;
57
58     /**
59      * A helper method for retrieving the application-wide SyncSetupManager.
60      * <p/>
61      * Can only be accessed on the main thread.
62      *
63      * @param context the ApplicationContext is retrieved from the context used as an argument.
64      * @return a singleton instance of the SyncSetupManager
65      */
66     public static ProfileSyncService get(Context context) {
67         ThreadUtils.assertOnUiThread();
68         if (sSyncSetupManager == null) {
69             sSyncSetupManager = new ProfileSyncService(context);
70         }
71         return sSyncSetupManager;
72     }
73
74     /**
75      * This is called pretty early in our application. Avoid any blocking operations here.
76      */
77     private ProfileSyncService(Context context) {
78         ThreadUtils.assertOnUiThread();
79         // We should store the application context, as we outlive any activity which may create us.
80         mContext = context.getApplicationContext();
81
82         // This may cause us to create ProfileSyncService even if sync has not
83         // been set up, but ProfileSyncService::Startup() won't be called until
84         // credentials are available.
85         mNativeProfileSyncServiceAndroid = nativeInit();
86     }
87
88     @CalledByNative
89     private static int getProfileSyncServiceAndroid(Context context) {
90         return get(context).mNativeProfileSyncServiceAndroid;
91     }
92
93     /**
94      * If we are currently in the process of setting up sync, this method clears the
95      * sync setup in progress flag.
96      */
97     @VisibleForTesting
98     public void finishSyncFirstSetupIfNeeded() {
99         if (isFirstSetupInProgress()) {
100             setSyncSetupCompleted();
101             setSetupInProgress(false);
102         }
103     }
104
105     public void signOut() {
106         nativeSignOutSync(mNativeProfileSyncServiceAndroid);
107     }
108
109     /**
110      * Signs in to sync, using the currently signed-in account.
111      */
112     public void syncSignIn() {
113         nativeSignInSync(mNativeProfileSyncServiceAndroid);
114         // Notify listeners right away that the sync state has changed (native side does not do
115         // this)
116         syncStateChanged();
117     }
118
119     /**
120      * Signs in to sync, using the existing auth token.
121      */
122     @Deprecated
123     public void syncSignIn(String account) {
124         syncSignIn();
125     }
126
127     /**
128      * Signs in to sync.
129      *
130      * @param account   The username of the account that is signing in.
131      * @param authToken Not used. ProfileSyncService switched to OAuth2 tokens.
132      * Deprecated. Use syncSignIn instead.
133      */
134     @Deprecated
135     public void syncSignInWithAuthToken(String account, String authToken) {
136         syncSignIn(account);
137     }
138
139     public void requestSyncFromNativeChrome(
140             int objectSource, String objectId, long version, String payload) {
141         ThreadUtils.assertOnUiThread();
142         nativeNudgeSyncer(
143                 mNativeProfileSyncServiceAndroid, objectSource, objectId, version, payload);
144     }
145
146     public void requestSyncFromNativeChromeForAllTypes() {
147         ThreadUtils.assertOnUiThread();
148         nativeNudgeSyncerForAllTypes(mNativeProfileSyncServiceAndroid);
149     }
150
151     /**
152      * Nudge the syncer to start a new sync cycle.
153      */
154     @VisibleForTesting
155     public void requestSyncCycleForTest() {
156         ThreadUtils.assertOnUiThread();
157         requestSyncFromNativeChromeForAllTypes();
158     }
159
160     public String querySyncStatus() {
161         ThreadUtils.assertOnUiThread();
162         return nativeQuerySyncStatusSummary(mNativeProfileSyncServiceAndroid);
163     }
164
165     /**
166      * Sets the the machine tag used by session sync to a unique value.
167      */
168     public void setSessionsId(UniqueIdentificationGenerator generator) {
169         ThreadUtils.assertOnUiThread();
170         String uniqueTag = generator.getUniqueId(null);
171         if (uniqueTag.isEmpty()) {
172             Log.e(TAG, "Unable to get unique tag for sync. " +
173                     "This may lead to unexpected tab sync behavior.");
174             return;
175         }
176         String sessionTag = SESSION_TAG_PREFIX + uniqueTag;
177         if (!nativeSetSyncSessionsId(mNativeProfileSyncServiceAndroid, sessionTag)) {
178             Log.e(TAG, "Unable to write session sync tag. " +
179                     "This may lead to unexpected tab sync behavior.");
180         }
181     }
182
183     /**
184      * Checks if a password or a passphrase is required for decryption of sync data.
185      * <p/>
186      * Returns NONE if the state is unavailable, or decryption passphrase/password is not required.
187      *
188      * @return the enum describing the decryption passphrase type required
189      */
190     public SyncDecryptionPassphraseType getSyncDecryptionPassphraseTypeIfRequired() {
191         // ProfileSyncService::IsUsingSecondaryPassphrase() requires the sync backend to be
192         // initialized, and that happens just after OnPassphraseRequired(). Therefore, we need to
193         // guard that call with a check of the sync backend since we can not be sure which
194         // passphrase type we should tell the user we need.
195         // This is tracked in:
196         // http://code.google.com/p/chromium/issues/detail?id=108127
197         if (isSyncInitialized() && isPassphraseRequiredForDecryption()) {
198             return getSyncDecryptionPassphraseType();
199         }
200         return SyncDecryptionPassphraseType.NONE;
201     }
202
203     /**
204      * Returns the actual passphrase type being used for encryption. The sync backend must be
205      * running (isSyncInitialized() returns true) before calling this function.
206      * <p/>
207      * This method should only be used if you want to know the raw value. For checking whether we
208      * should ask the user for a passphrase, you should instead use
209      * getSyncDecryptionPassphraseTypeIfRequired().
210      */
211     public SyncDecryptionPassphraseType getSyncDecryptionPassphraseType() {
212         assert isSyncInitialized();
213         int passphraseType = nativeGetPassphraseType(mNativeProfileSyncServiceAndroid);
214         return SyncDecryptionPassphraseType.fromInternalValue(passphraseType);
215     }
216
217     public boolean isSyncKeystoreMigrationDone() {
218         assert isSyncInitialized();
219         return nativeIsSyncKeystoreMigrationDone(mNativeProfileSyncServiceAndroid);
220     }
221
222     /**
223      * Returns true if the current explicit passphrase time is defined.
224      */
225     public boolean hasExplicitPassphraseTime() {
226         assert isSyncInitialized();
227         return nativeHasExplicitPassphraseTime(mNativeProfileSyncServiceAndroid);
228     }
229
230     public String getSyncEnterGooglePassphraseBodyWithDateText() {
231         assert isSyncInitialized();
232         return nativeGetSyncEnterGooglePassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
233     }
234
235     public String getSyncEnterCustomPassphraseBodyWithDateText() {
236         assert isSyncInitialized();
237         return nativeGetSyncEnterCustomPassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
238     }
239
240     public String getCurrentSignedInAccountText() {
241         assert isSyncInitialized();
242         return nativeGetCurrentSignedInAccountText(mNativeProfileSyncServiceAndroid);
243     }
244
245     public String getSyncEnterCustomPassphraseBodyText() {
246         return nativeGetSyncEnterCustomPassphraseBodyText(mNativeProfileSyncServiceAndroid);
247     }
248
249     /**
250      * Checks if sync is currently set to use a custom passphrase. The sync backend must be running
251      * (isSyncInitialized() returns true) before calling this function.
252      *
253      * @return true if sync is using a custom passphrase.
254      */
255     public boolean isUsingSecondaryPassphrase() {
256         assert isSyncInitialized();
257         return nativeIsUsingSecondaryPassphrase(mNativeProfileSyncServiceAndroid);
258     }
259
260     /**
261      * Checks if we need a passphrase to decrypt a currently-enabled data type. This returns false
262      * if a passphrase is needed for a type that is not currently enabled.
263      *
264      * @return true if we need a passphrase.
265      */
266     public boolean isPassphraseRequiredForDecryption() {
267         assert isSyncInitialized();
268         return nativeIsPassphraseRequiredForDecryption(mNativeProfileSyncServiceAndroid);
269     }
270
271     /**
272      * Checks if we need a passphrase to decrypt any data type (including types that aren't
273      * currently enabled or supported, such as passwords). This API is used to determine if we
274      * need to provide a decryption passphrase before we can re-encrypt with a custom passphrase.
275      *
276      * @return true if we need a passphrase for some type.
277      */
278     public boolean isPassphraseRequiredForExternalType() {
279         assert isSyncInitialized();
280         return nativeIsPassphraseRequiredForExternalType(mNativeProfileSyncServiceAndroid);
281     }
282
283     /**
284      * Checks if the sync backend is running.
285      *
286      * @return true if sync is initialized/running.
287      */
288     public boolean isSyncInitialized() {
289         return nativeIsSyncInitialized(mNativeProfileSyncServiceAndroid);
290     }
291
292     /**
293      * Checks if the first sync setup is currently in progress.
294      *
295      * @return true if first sync setup is in progress
296      */
297     public boolean isFirstSetupInProgress() {
298         return nativeIsFirstSetupInProgress(mNativeProfileSyncServiceAndroid);
299     }
300
301     /**
302      * Checks if the all the data types are encrypted.
303      *
304      * @return true if all data types are encrypted, false if only passwords are encrypted.
305      */
306     public boolean isEncryptEverythingEnabled() {
307         assert isSyncInitialized();
308         return nativeIsEncryptEverythingEnabled(mNativeProfileSyncServiceAndroid);
309     }
310
311     /**
312      * Turns on encryption of all data types. This only takes effect after sync configuration is
313      * completed and setPreferredDataTypes() is invoked.
314      */
315     public void enableEncryptEverything() {
316         assert isSyncInitialized();
317         nativeEnableEncryptEverything(mNativeProfileSyncServiceAndroid);
318     }
319
320     public void setEncryptionPassphrase(String passphrase, boolean isGaia) {
321         assert isSyncInitialized();
322         nativeSetEncryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase, isGaia);
323     }
324
325     public boolean isCryptographerReady() {
326         assert isSyncInitialized();
327         return nativeIsCryptographerReady(mNativeProfileSyncServiceAndroid);
328     }
329
330     public boolean setDecryptionPassphrase(String passphrase) {
331         assert isSyncInitialized();
332         return nativeSetDecryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase);
333     }
334
335     public GoogleServiceAuthError.State getAuthError() {
336         int authErrorCode = nativeGetAuthError(mNativeProfileSyncServiceAndroid);
337         return GoogleServiceAuthError.State.fromCode(authErrorCode);
338     }
339
340     /**
341      * Gets the set of data types that are currently enabled to sync.
342      *
343      * @return Set of enabled types.
344      */
345     public Set<ModelType> getPreferredDataTypes() {
346         long modelTypeSelection =
347             nativeGetEnabledDataTypes(mNativeProfileSyncServiceAndroid);
348         Set<ModelType> syncTypes = new HashSet<ModelType>();
349         if ((modelTypeSelection & ModelTypeSelection.AUTOFILL) != 0) {
350           syncTypes.add(ModelType.AUTOFILL);
351         }
352         if ((modelTypeSelection & ModelTypeSelection.AUTOFILL_PROFILE) != 0) {
353           syncTypes.add(ModelType.AUTOFILL_PROFILE);
354         }
355         if ((modelTypeSelection & ModelTypeSelection.BOOKMARK) != 0) {
356           syncTypes.add(ModelType.BOOKMARK);
357         }
358         if ((modelTypeSelection & ModelTypeSelection.EXPERIMENTS) != 0) {
359           syncTypes.add(ModelType.EXPERIMENTS);
360         }
361         if ((modelTypeSelection & ModelTypeSelection.NIGORI) != 0) {
362           syncTypes.add(ModelType.NIGORI);
363         }
364         if ((modelTypeSelection & ModelTypeSelection.PASSWORD) != 0) {
365           syncTypes.add(ModelType.PASSWORD);
366         }
367         if ((modelTypeSelection & ModelTypeSelection.SESSION) != 0) {
368           syncTypes.add(ModelType.SESSION);
369         }
370         if ((modelTypeSelection & ModelTypeSelection.TYPED_URL) != 0) {
371           syncTypes.add(ModelType.TYPED_URL);
372         }
373         if ((modelTypeSelection & ModelTypeSelection.HISTORY_DELETE_DIRECTIVE) != 0) {
374           syncTypes.add(ModelType.HISTORY_DELETE_DIRECTIVE);
375         }
376         if ((modelTypeSelection & ModelTypeSelection.DEVICE_INFO) != 0) {
377           syncTypes.add(ModelType.DEVICE_INFO);
378         }
379         if ((modelTypeSelection & ModelTypeSelection.PROXY_TABS) != 0) {
380           syncTypes.add(ModelType.PROXY_TABS);
381         }
382         if ((modelTypeSelection & ModelTypeSelection.FAVICON_IMAGE) != 0) {
383           syncTypes.add(ModelType.FAVICON_IMAGE);
384         }
385         if ((modelTypeSelection & ModelTypeSelection.FAVICON_TRACKING) != 0) {
386           syncTypes.add(ModelType.FAVICON_TRACKING);
387         }
388         return syncTypes;
389     }
390
391     public boolean hasKeepEverythingSynced() {
392         return nativeHasKeepEverythingSynced(mNativeProfileSyncServiceAndroid);
393     }
394
395     /**
396      * Enables syncing for the passed data types.
397      *
398      * @param syncEverything Set to true if the user wants to sync all data types
399      *                       (including new data types we add in the future).
400      * @param enabledTypes   The set of types to enable. Ignored (can be null) if
401      *                       syncEverything is true.
402      */
403     public void setPreferredDataTypes(boolean syncEverything, Set<ModelType> enabledTypes) {
404         long modelTypeSelection = 0;
405         if (syncEverything || enabledTypes.contains(ModelType.AUTOFILL)) {
406             modelTypeSelection |= ModelTypeSelection.AUTOFILL;
407         }
408         if (syncEverything || enabledTypes.contains(ModelType.BOOKMARK)) {
409             modelTypeSelection |= ModelTypeSelection.BOOKMARK;
410         }
411         if (syncEverything || enabledTypes.contains(ModelType.PASSWORD)) {
412             modelTypeSelection |= ModelTypeSelection.PASSWORD;
413         }
414         if (syncEverything || enabledTypes.contains(ModelType.PROXY_TABS)) {
415             modelTypeSelection |= ModelTypeSelection.PROXY_TABS;
416         }
417         if (syncEverything || enabledTypes.contains(ModelType.TYPED_URL)) {
418             modelTypeSelection |= ModelTypeSelection.TYPED_URL;
419         }
420         nativeSetPreferredDataTypes(
421                 mNativeProfileSyncServiceAndroid, syncEverything, modelTypeSelection);
422     }
423
424     public void setSyncSetupCompleted() {
425         nativeSetSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
426     }
427
428     public boolean hasSyncSetupCompleted() {
429         return nativeHasSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
430     }
431
432     public boolean isStartSuppressed() {
433         return nativeIsStartSuppressed(mNativeProfileSyncServiceAndroid);
434     }
435
436     /**
437      * Notifies sync whether sync setup is in progress - this tells sync whether it should start
438      * syncing data types when it starts up, or if it should just stay in "configuration mode".
439      *
440      * @param inProgress True to put sync in configuration mode, false to turn off configuration
441      *                   and allow syncing.
442      */
443     public void setSetupInProgress(boolean inProgress) {
444         nativeSetSetupInProgress(mNativeProfileSyncServiceAndroid, inProgress);
445     }
446
447     public void addSyncStateChangedListener(SyncStateChangedListener listener) {
448         ThreadUtils.assertOnUiThread();
449         mListeners.add(listener);
450     }
451
452     public void removeSyncStateChangedListener(SyncStateChangedListener listener) {
453         ThreadUtils.assertOnUiThread();
454         mListeners.remove(listener);
455     }
456
457     public boolean hasUnrecoverableError() {
458         return nativeHasUnrecoverableError(mNativeProfileSyncServiceAndroid);
459     }
460
461     /**
462      * Called when the state of the native sync engine has changed, so various
463      * UI elements can update themselves.
464      */
465     @CalledByNative
466     public void syncStateChanged() {
467         if (!mListeners.isEmpty()) {
468             for (SyncStateChangedListener listener : mListeners) {
469                 listener.syncStateChanged();
470             }
471         }
472     }
473
474     @VisibleForTesting
475     public String getSyncInternalsInfoForTest() {
476         ThreadUtils.assertOnUiThread();
477         return nativeGetAboutInfoForTest(mNativeProfileSyncServiceAndroid);
478     }
479
480     /**
481      * Starts the sync engine.
482      */
483     public void enableSync() {
484         nativeEnableSync(mNativeProfileSyncServiceAndroid);
485     }
486
487     /**
488      * Stops the sync engine.
489      */
490     public void disableSync() {
491         nativeDisableSync(mNativeProfileSyncServiceAndroid);
492     }
493
494     /**
495      * Returns the time when the last sync cycle was completed.
496      *
497      * @return The difference measured in microseconds, between last sync cycle completion time
498      * and 1 January 1970 00:00:00 UTC.
499      */
500     public long getLastSyncedTimeForTest() {
501         return nativeGetLastSyncedTimeForTest(mNativeProfileSyncServiceAndroid);
502     }
503
504     // Native methods
505     private native void nativeNudgeSyncer(
506             int nativeProfileSyncServiceAndroid, int objectSource, String objectId, long version,
507             String payload);
508     private native void nativeNudgeSyncerForAllTypes(int nativeProfileSyncServiceAndroid);
509     private native int nativeInit();
510     private native void nativeEnableSync(int nativeProfileSyncServiceAndroid);
511     private native void nativeDisableSync(int nativeProfileSyncServiceAndroid);
512     private native void nativeSignInSync(int nativeProfileSyncServiceAndroid);
513     private native void nativeSignOutSync(int nativeProfileSyncServiceAndroid);
514     private native boolean nativeSetSyncSessionsId(int nativeProfileSyncServiceAndroid, String tag);
515     private native String nativeQuerySyncStatusSummary(int nativeProfileSyncServiceAndroid);
516     private native int nativeGetAuthError(int nativeProfileSyncServiceAndroid);
517     private native boolean nativeIsSyncInitialized(int nativeProfileSyncServiceAndroid);
518     private native boolean nativeIsFirstSetupInProgress(int nativeProfileSyncServiceAndroid);
519     private native boolean nativeIsEncryptEverythingEnabled(int nativeProfileSyncServiceAndroid);
520     private native void nativeEnableEncryptEverything(int nativeProfileSyncServiceAndroid);
521     private native boolean nativeIsPassphraseRequiredForDecryption(
522             int nativeProfileSyncServiceAndroid);
523     private native boolean nativeIsPassphraseRequiredForExternalType(
524             int nativeProfileSyncServiceAndroid);
525     private native boolean nativeIsUsingSecondaryPassphrase(int nativeProfileSyncServiceAndroid);
526     private native boolean nativeSetDecryptionPassphrase(
527             int nativeProfileSyncServiceAndroid, String passphrase);
528     private native void nativeSetEncryptionPassphrase(
529             int nativeProfileSyncServiceAndroid, String passphrase, boolean isGaia);
530     private native boolean nativeIsCryptographerReady(int nativeProfileSyncServiceAndroid);
531     private native int nativeGetPassphraseType(int nativeProfileSyncServiceAndroid);
532     private native boolean nativeHasExplicitPassphraseTime(int nativeProfileSyncServiceAndroid);
533     private native String nativeGetSyncEnterGooglePassphraseBodyWithDateText(
534             int nativeProfileSyncServiceAndroid);
535     private native String nativeGetSyncEnterCustomPassphraseBodyWithDateText(
536             int nativeProfileSyncServiceAndroid);
537     private native String nativeGetCurrentSignedInAccountText(int nativeProfileSyncServiceAndroid);
538     private native String nativeGetSyncEnterCustomPassphraseBodyText(
539             int nativeProfileSyncServiceAndroid);
540     private native boolean nativeIsSyncKeystoreMigrationDone(int nativeProfileSyncServiceAndroid);
541     private native long nativeGetEnabledDataTypes(
542         int nativeProfileSyncServiceAndroid);
543     private native void nativeSetPreferredDataTypes(
544             int nativeProfileSyncServiceAndroid, boolean syncEverything, long modelTypeSelection);
545     private native void nativeSetSetupInProgress(
546             int nativeProfileSyncServiceAndroid, boolean inProgress);
547     private native void nativeSetSyncSetupCompleted(int nativeProfileSyncServiceAndroid);
548     private native boolean nativeHasSyncSetupCompleted(int nativeProfileSyncServiceAndroid);
549     private native boolean nativeIsStartSuppressed(int nativeProfileSyncServiceAndroid);
550     private native boolean nativeHasKeepEverythingSynced(int nativeProfileSyncServiceAndroid);
551     private native boolean nativeHasUnrecoverableError(int nativeProfileSyncServiceAndroid);
552     private native String nativeGetAboutInfoForTest(int nativeProfileSyncServiceAndroid);
553     private native long nativeGetLastSyncedTimeForTest(int nativeProfileSyncServiceAndroid);
554 }