Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java

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

Powered by Google App Engine
This is Rietveld 408576698