root/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. syncStateChanged
  2. get
  3. getProfileSyncServiceAndroid
  4. finishSyncFirstSetupIfNeeded
  5. signOut
  6. syncSignIn
  7. syncSignIn
  8. syncSignInWithAuthToken
  9. requestSyncFromNativeChrome
  10. requestSyncFromNativeChromeForAllTypes
  11. requestSyncCycleForTest
  12. querySyncStatus
  13. setSessionsId
  14. getSyncDecryptionPassphraseTypeIfRequired
  15. getSyncDecryptionPassphraseType
  16. isSyncKeystoreMigrationDone
  17. hasExplicitPassphraseTime
  18. getSyncEnterGooglePassphraseBodyWithDateText
  19. getSyncEnterCustomPassphraseBodyWithDateText
  20. getCurrentSignedInAccountText
  21. getSyncEnterCustomPassphraseBodyText
  22. isUsingSecondaryPassphrase
  23. isPassphraseRequiredForDecryption
  24. isPassphraseRequiredForExternalType
  25. isSyncInitialized
  26. isFirstSetupInProgress
  27. isEncryptEverythingEnabled
  28. enableEncryptEverything
  29. setEncryptionPassphrase
  30. isCryptographerReady
  31. setDecryptionPassphrase
  32. getAuthError
  33. getPreferredDataTypes
  34. hasKeepEverythingSynced
  35. setPreferredDataTypes
  36. setSyncSetupCompleted
  37. hasSyncSetupCompleted
  38. isStartSuppressed
  39. setSetupInProgress
  40. addSyncStateChangedListener
  41. removeSyncStateChangedListener
  42. hasUnrecoverableError
  43. syncStateChanged
  44. getSyncInternalsInfoForTest
  45. enableSync
  46. disableSync
  47. getLastSyncedTimeForTest
  48. nativeNudgeSyncer
  49. nativeNudgeSyncerForAllTypes
  50. nativeInit
  51. nativeEnableSync
  52. nativeDisableSync
  53. nativeSignInSync
  54. nativeSignOutSync
  55. nativeSetSyncSessionsId
  56. nativeQuerySyncStatusSummary
  57. nativeGetAuthError
  58. nativeIsSyncInitialized
  59. nativeIsFirstSetupInProgress
  60. nativeIsEncryptEverythingEnabled
  61. nativeEnableEncryptEverything
  62. nativeIsPassphraseRequiredForDecryption
  63. nativeIsPassphraseRequiredForExternalType
  64. nativeIsUsingSecondaryPassphrase
  65. nativeSetDecryptionPassphrase
  66. nativeSetEncryptionPassphrase
  67. nativeIsCryptographerReady
  68. nativeGetPassphraseType
  69. nativeHasExplicitPassphraseTime
  70. nativeGetSyncEnterGooglePassphraseBodyWithDateText
  71. nativeGetSyncEnterCustomPassphraseBodyWithDateText
  72. nativeGetCurrentSignedInAccountText
  73. nativeGetSyncEnterCustomPassphraseBodyText
  74. nativeIsSyncKeystoreMigrationDone
  75. nativeGetEnabledDataTypes
  76. nativeSetPreferredDataTypes
  77. nativeSetSetupInProgress
  78. nativeSetSyncSetupCompleted
  79. nativeHasSyncSetupCompleted
  80. nativeIsStartSuppressed
  81. nativeHasKeepEverythingSynced
  82. nativeHasUnrecoverableError
  83. nativeGetAboutInfoForTest
  84. nativeGetLastSyncedTimeForTest

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.sync;

import android.content.Context;
import android.util.Log;

import com.google.common.annotations.VisibleForTesting;

import org.chromium.base.CalledByNative;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.identity.UniqueIdentificationGenerator;
import org.chromium.sync.internal_api.pub.SyncDecryptionPassphraseType;
import org.chromium.sync.internal_api.pub.base.ModelType;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Android wrapper of the ProfileSyncService which provides access from the Java layer.
 * <p/>
 * This class mostly wraps native classes, but it make a few business logic decisions, both in Java
 * and in native.
 * <p/>
 * Only usable from the UI thread as the native ProfileSyncService requires its access to be in the
 * UI thread.
 * <p/>
 * See chrome/browser/sync/profile_sync_service.h for more details.
 */
public class ProfileSyncService {

    public interface SyncStateChangedListener {
        // Invoked when the underlying sync status has changed.
        public void syncStateChanged();
    }

    private static final String TAG = "ProfileSyncService";

    @VisibleForTesting
    public static final String SESSION_TAG_PREFIX = "session_sync";

    private static ProfileSyncService sSyncSetupManager;

    @VisibleForTesting
    protected final Context mContext;

    // Sync state changes more often than listeners are added/removed, so using CopyOnWrite.
    private final List<SyncStateChangedListener> mListeners =
            new CopyOnWriteArrayList<SyncStateChangedListener>();

    // Native ProfileSyncServiceAndroid object. Can not be final since we set it to 0 in destroy().
    private final long mNativeProfileSyncServiceAndroid;

    /**
     * A helper method for retrieving the application-wide SyncSetupManager.
     * <p/>
     * Can only be accessed on the main thread.
     *
     * @param context the ApplicationContext is retrieved from the context used as an argument.
     * @return a singleton instance of the SyncSetupManager
     */
    public static ProfileSyncService get(Context context) {
        ThreadUtils.assertOnUiThread();
        if (sSyncSetupManager == null) {
            sSyncSetupManager = new ProfileSyncService(context);
        }
        return sSyncSetupManager;
    }

    /**
     * This is called pretty early in our application. Avoid any blocking operations here.
     */
    private ProfileSyncService(Context context) {
        ThreadUtils.assertOnUiThread();
        // We should store the application context, as we outlive any activity which may create us.
        mContext = context.getApplicationContext();

        // This may cause us to create ProfileSyncService even if sync has not
        // been set up, but ProfileSyncService::Startup() won't be called until
        // credentials are available.
        mNativeProfileSyncServiceAndroid = nativeInit();
    }

    @CalledByNative
    private static long getProfileSyncServiceAndroid(Context context) {
        return get(context).mNativeProfileSyncServiceAndroid;
    }

    /**
     * If we are currently in the process of setting up sync, this method clears the
     * sync setup in progress flag.
     */
    @VisibleForTesting
    public void finishSyncFirstSetupIfNeeded() {
        if (isFirstSetupInProgress()) {
            setSyncSetupCompleted();
            setSetupInProgress(false);
        }
    }

    public void signOut() {
        nativeSignOutSync(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Signs in to sync, using the currently signed-in account.
     */
    public void syncSignIn() {
        nativeSignInSync(mNativeProfileSyncServiceAndroid);
        // Notify listeners right away that the sync state has changed (native side does not do
        // this)
        syncStateChanged();
    }

    /**
     * Signs in to sync, using the existing auth token.
     */
    @Deprecated
    public void syncSignIn(String account) {
        syncSignIn();
    }

    /**
     * Signs in to sync.
     *
     * @param account   The username of the account that is signing in.
     * @param authToken Not used. ProfileSyncService switched to OAuth2 tokens.
     * Deprecated. Use syncSignIn instead.
     */
    @Deprecated
    public void syncSignInWithAuthToken(String account, String authToken) {
        syncSignIn(account);
    }

    public void requestSyncFromNativeChrome(
            int objectSource, String objectId, long version, String payload) {
        ThreadUtils.assertOnUiThread();
        nativeNudgeSyncer(
                mNativeProfileSyncServiceAndroid, objectSource, objectId, version, payload);
    }

    public void requestSyncFromNativeChromeForAllTypes() {
        ThreadUtils.assertOnUiThread();
        nativeNudgeSyncerForAllTypes(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Nudge the syncer to start a new sync cycle.
     */
    @VisibleForTesting
    public void requestSyncCycleForTest() {
        ThreadUtils.assertOnUiThread();
        requestSyncFromNativeChromeForAllTypes();
    }

    public String querySyncStatus() {
        ThreadUtils.assertOnUiThread();
        return nativeQuerySyncStatusSummary(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Sets the the machine tag used by session sync to a unique value.
     */
    public void setSessionsId(UniqueIdentificationGenerator generator) {
        ThreadUtils.assertOnUiThread();
        String uniqueTag = generator.getUniqueId(null);
        if (uniqueTag.isEmpty()) {
            Log.e(TAG, "Unable to get unique tag for sync. " +
                    "This may lead to unexpected tab sync behavior.");
            return;
        }
        String sessionTag = SESSION_TAG_PREFIX + uniqueTag;
        if (!nativeSetSyncSessionsId(mNativeProfileSyncServiceAndroid, sessionTag)) {
            Log.e(TAG, "Unable to write session sync tag. " +
                    "This may lead to unexpected tab sync behavior.");
        }
    }

    /**
     * Checks if a password or a passphrase is required for decryption of sync data.
     * <p/>
     * Returns NONE if the state is unavailable, or decryption passphrase/password is not required.
     *
     * @return the enum describing the decryption passphrase type required
     */
    public SyncDecryptionPassphraseType getSyncDecryptionPassphraseTypeIfRequired() {
        // ProfileSyncService::IsUsingSecondaryPassphrase() requires the sync backend to be
        // initialized, and that happens just after OnPassphraseRequired(). Therefore, we need to
        // guard that call with a check of the sync backend since we can not be sure which
        // passphrase type we should tell the user we need.
        // This is tracked in:
        // http://code.google.com/p/chromium/issues/detail?id=108127
        if (isSyncInitialized() && isPassphraseRequiredForDecryption()) {
            return getSyncDecryptionPassphraseType();
        }
        return SyncDecryptionPassphraseType.NONE;
    }

    /**
     * Returns the actual passphrase type being used for encryption. The sync backend must be
     * running (isSyncInitialized() returns true) before calling this function.
     * <p/>
     * This method should only be used if you want to know the raw value. For checking whether we
     * should ask the user for a passphrase, you should instead use
     * getSyncDecryptionPassphraseTypeIfRequired().
     */
    public SyncDecryptionPassphraseType getSyncDecryptionPassphraseType() {
        assert isSyncInitialized();
        int passphraseType = nativeGetPassphraseType(mNativeProfileSyncServiceAndroid);
        return SyncDecryptionPassphraseType.fromInternalValue(passphraseType);
    }

    public boolean isSyncKeystoreMigrationDone() {
        assert isSyncInitialized();
        return nativeIsSyncKeystoreMigrationDone(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Returns true if the current explicit passphrase time is defined.
     */
    public boolean hasExplicitPassphraseTime() {
        assert isSyncInitialized();
        return nativeHasExplicitPassphraseTime(mNativeProfileSyncServiceAndroid);
    }

    public String getSyncEnterGooglePassphraseBodyWithDateText() {
        assert isSyncInitialized();
        return nativeGetSyncEnterGooglePassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
    }

    public String getSyncEnterCustomPassphraseBodyWithDateText() {
        assert isSyncInitialized();
        return nativeGetSyncEnterCustomPassphraseBodyWithDateText(mNativeProfileSyncServiceAndroid);
    }

    public String getCurrentSignedInAccountText() {
        assert isSyncInitialized();
        return nativeGetCurrentSignedInAccountText(mNativeProfileSyncServiceAndroid);
    }

    public String getSyncEnterCustomPassphraseBodyText() {
        return nativeGetSyncEnterCustomPassphraseBodyText(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if sync is currently set to use a custom passphrase. The sync backend must be running
     * (isSyncInitialized() returns true) before calling this function.
     *
     * @return true if sync is using a custom passphrase.
     */
    public boolean isUsingSecondaryPassphrase() {
        assert isSyncInitialized();
        return nativeIsUsingSecondaryPassphrase(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if we need a passphrase to decrypt a currently-enabled data type. This returns false
     * if a passphrase is needed for a type that is not currently enabled.
     *
     * @return true if we need a passphrase.
     */
    public boolean isPassphraseRequiredForDecryption() {
        assert isSyncInitialized();
        return nativeIsPassphraseRequiredForDecryption(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if we need a passphrase to decrypt any data type (including types that aren't
     * currently enabled or supported, such as passwords). This API is used to determine if we
     * need to provide a decryption passphrase before we can re-encrypt with a custom passphrase.
     *
     * @return true if we need a passphrase for some type.
     */
    public boolean isPassphraseRequiredForExternalType() {
        assert isSyncInitialized();
        return nativeIsPassphraseRequiredForExternalType(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if the sync backend is running.
     *
     * @return true if sync is initialized/running.
     */
    public boolean isSyncInitialized() {
        return nativeIsSyncInitialized(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if the first sync setup is currently in progress.
     *
     * @return true if first sync setup is in progress
     */
    public boolean isFirstSetupInProgress() {
        return nativeIsFirstSetupInProgress(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Checks if the all the data types are encrypted.
     *
     * @return true if all data types are encrypted, false if only passwords are encrypted.
     */
    public boolean isEncryptEverythingEnabled() {
        assert isSyncInitialized();
        return nativeIsEncryptEverythingEnabled(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Turns on encryption of all data types. This only takes effect after sync configuration is
     * completed and setPreferredDataTypes() is invoked.
     */
    public void enableEncryptEverything() {
        assert isSyncInitialized();
        nativeEnableEncryptEverything(mNativeProfileSyncServiceAndroid);
    }

    public void setEncryptionPassphrase(String passphrase, boolean isGaia) {
        assert isSyncInitialized();
        nativeSetEncryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase, isGaia);
    }

    public boolean isCryptographerReady() {
        assert isSyncInitialized();
        return nativeIsCryptographerReady(mNativeProfileSyncServiceAndroid);
    }

    public boolean setDecryptionPassphrase(String passphrase) {
        assert isSyncInitialized();
        return nativeSetDecryptionPassphrase(mNativeProfileSyncServiceAndroid, passphrase);
    }

    public GoogleServiceAuthError.State getAuthError() {
        int authErrorCode = nativeGetAuthError(mNativeProfileSyncServiceAndroid);
        return GoogleServiceAuthError.State.fromCode(authErrorCode);
    }

    /**
     * Gets the set of data types that are currently enabled to sync.
     *
     * @return Set of enabled types.
     */
    public Set<ModelType> getPreferredDataTypes() {
        long modelTypeSelection =
                nativeGetEnabledDataTypes(mNativeProfileSyncServiceAndroid);
        Set<ModelType> syncTypes = new HashSet<ModelType>();
        if ((modelTypeSelection & ModelTypeSelection.AUTOFILL) != 0) {
            syncTypes.add(ModelType.AUTOFILL);
        }
        if ((modelTypeSelection & ModelTypeSelection.AUTOFILL_PROFILE) != 0) {
            syncTypes.add(ModelType.AUTOFILL_PROFILE);
        }
        if ((modelTypeSelection & ModelTypeSelection.BOOKMARK) != 0) {
            syncTypes.add(ModelType.BOOKMARK);
        }
        if ((modelTypeSelection & ModelTypeSelection.EXPERIMENTS) != 0) {
            syncTypes.add(ModelType.EXPERIMENTS);
        }
        if ((modelTypeSelection & ModelTypeSelection.NIGORI) != 0) {
            syncTypes.add(ModelType.NIGORI);
        }
        if ((modelTypeSelection & ModelTypeSelection.PASSWORD) != 0) {
            syncTypes.add(ModelType.PASSWORD);
        }
        if ((modelTypeSelection & ModelTypeSelection.SESSION) != 0) {
            syncTypes.add(ModelType.SESSION);
        }
        if ((modelTypeSelection & ModelTypeSelection.TYPED_URL) != 0) {
            syncTypes.add(ModelType.TYPED_URL);
        }
        if ((modelTypeSelection & ModelTypeSelection.HISTORY_DELETE_DIRECTIVE) != 0) {
            syncTypes.add(ModelType.HISTORY_DELETE_DIRECTIVE);
        }
        if ((modelTypeSelection & ModelTypeSelection.DEVICE_INFO) != 0) {
            syncTypes.add(ModelType.DEVICE_INFO);
        }
        if ((modelTypeSelection & ModelTypeSelection.PROXY_TABS) != 0) {
            syncTypes.add(ModelType.PROXY_TABS);
        }
        if ((modelTypeSelection & ModelTypeSelection.FAVICON_IMAGE) != 0) {
            syncTypes.add(ModelType.FAVICON_IMAGE);
        }
        if ((modelTypeSelection & ModelTypeSelection.FAVICON_TRACKING) != 0) {
            syncTypes.add(ModelType.FAVICON_TRACKING);
        }
        return syncTypes;
    }

    public boolean hasKeepEverythingSynced() {
        return nativeHasKeepEverythingSynced(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Enables syncing for the passed data types.
     *
     * @param syncEverything Set to true if the user wants to sync all data types
     *                       (including new data types we add in the future).
     * @param enabledTypes   The set of types to enable. Ignored (can be null) if
     *                       syncEverything is true.
     */
    public void setPreferredDataTypes(boolean syncEverything, Set<ModelType> enabledTypes) {
        long modelTypeSelection = 0;
        if (syncEverything || enabledTypes.contains(ModelType.AUTOFILL)) {
            modelTypeSelection |= ModelTypeSelection.AUTOFILL;
        }
        if (syncEverything || enabledTypes.contains(ModelType.BOOKMARK)) {
            modelTypeSelection |= ModelTypeSelection.BOOKMARK;
        }
        if (syncEverything || enabledTypes.contains(ModelType.PASSWORD)) {
            modelTypeSelection |= ModelTypeSelection.PASSWORD;
        }
        if (syncEverything || enabledTypes.contains(ModelType.PROXY_TABS)) {
            modelTypeSelection |= ModelTypeSelection.PROXY_TABS;
        }
        if (syncEverything || enabledTypes.contains(ModelType.TYPED_URL)) {
            modelTypeSelection |= ModelTypeSelection.TYPED_URL;
        }
        nativeSetPreferredDataTypes(
                mNativeProfileSyncServiceAndroid, syncEverything, modelTypeSelection);
    }

    public void setSyncSetupCompleted() {
        nativeSetSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
    }

    public boolean hasSyncSetupCompleted() {
        return nativeHasSyncSetupCompleted(mNativeProfileSyncServiceAndroid);
    }

    public boolean isStartSuppressed() {
        return nativeIsStartSuppressed(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Notifies sync whether sync setup is in progress - this tells sync whether it should start
     * syncing data types when it starts up, or if it should just stay in "configuration mode".
     *
     * @param inProgress True to put sync in configuration mode, false to turn off configuration
     *                   and allow syncing.
     */
    public void setSetupInProgress(boolean inProgress) {
        nativeSetSetupInProgress(mNativeProfileSyncServiceAndroid, inProgress);
    }

    public void addSyncStateChangedListener(SyncStateChangedListener listener) {
        ThreadUtils.assertOnUiThread();
        mListeners.add(listener);
    }

    public void removeSyncStateChangedListener(SyncStateChangedListener listener) {
        ThreadUtils.assertOnUiThread();
        mListeners.remove(listener);
    }

    public boolean hasUnrecoverableError() {
        return nativeHasUnrecoverableError(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Called when the state of the native sync engine has changed, so various
     * UI elements can update themselves.
     */
    @CalledByNative
    public void syncStateChanged() {
        if (!mListeners.isEmpty()) {
            for (SyncStateChangedListener listener : mListeners) {
                listener.syncStateChanged();
            }
        }
    }

    @VisibleForTesting
    public String getSyncInternalsInfoForTest() {
        ThreadUtils.assertOnUiThread();
        return nativeGetAboutInfoForTest(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Starts the sync engine.
     */
    public void enableSync() {
        nativeEnableSync(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Stops the sync engine.
     */
    public void disableSync() {
        nativeDisableSync(mNativeProfileSyncServiceAndroid);
    }

    /**
     * Returns the time when the last sync cycle was completed.
     *
     * @return The difference measured in microseconds, between last sync cycle completion time
     * and 1 January 1970 00:00:00 UTC.
     */
    public long getLastSyncedTimeForTest() {
        return nativeGetLastSyncedTimeForTest(mNativeProfileSyncServiceAndroid);
    }

    // Native methods
    private native void nativeNudgeSyncer(
            long nativeProfileSyncServiceAndroid, int objectSource, String objectId, long version,
            String payload);
    private native void nativeNudgeSyncerForAllTypes(long nativeProfileSyncServiceAndroid);
    private native long nativeInit();
    private native void nativeEnableSync(long nativeProfileSyncServiceAndroid);
    private native void nativeDisableSync(long nativeProfileSyncServiceAndroid);
    private native void nativeSignInSync(long nativeProfileSyncServiceAndroid);
    private native void nativeSignOutSync(long nativeProfileSyncServiceAndroid);
    private native boolean nativeSetSyncSessionsId(
            long nativeProfileSyncServiceAndroid, String tag);
    private native String nativeQuerySyncStatusSummary(long nativeProfileSyncServiceAndroid);
    private native int nativeGetAuthError(long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsSyncInitialized(long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsFirstSetupInProgress(long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsEncryptEverythingEnabled(long nativeProfileSyncServiceAndroid);
    private native void nativeEnableEncryptEverything(long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsPassphraseRequiredForDecryption(
            long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsPassphraseRequiredForExternalType(
            long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsUsingSecondaryPassphrase(long nativeProfileSyncServiceAndroid);
    private native boolean nativeSetDecryptionPassphrase(
            long nativeProfileSyncServiceAndroid, String passphrase);
    private native void nativeSetEncryptionPassphrase(
            long nativeProfileSyncServiceAndroid, String passphrase, boolean isGaia);
    private native boolean nativeIsCryptographerReady(long nativeProfileSyncServiceAndroid);
    private native int nativeGetPassphraseType(long nativeProfileSyncServiceAndroid);
    private native boolean nativeHasExplicitPassphraseTime(long nativeProfileSyncServiceAndroid);
    private native String nativeGetSyncEnterGooglePassphraseBodyWithDateText(
            long nativeProfileSyncServiceAndroid);
    private native String nativeGetSyncEnterCustomPassphraseBodyWithDateText(
            long nativeProfileSyncServiceAndroid);
    private native String nativeGetCurrentSignedInAccountText(long nativeProfileSyncServiceAndroid);
    private native String nativeGetSyncEnterCustomPassphraseBodyText(
            long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsSyncKeystoreMigrationDone(long nativeProfileSyncServiceAndroid);
    private native long nativeGetEnabledDataTypes(
        long nativeProfileSyncServiceAndroid);
    private native void nativeSetPreferredDataTypes(
            long nativeProfileSyncServiceAndroid, boolean syncEverything, long modelTypeSelection);
    private native void nativeSetSetupInProgress(
            long nativeProfileSyncServiceAndroid, boolean inProgress);
    private native void nativeSetSyncSetupCompleted(long nativeProfileSyncServiceAndroid);
    private native boolean nativeHasSyncSetupCompleted(long nativeProfileSyncServiceAndroid);
    private native boolean nativeIsStartSuppressed(long nativeProfileSyncServiceAndroid);
    private native boolean nativeHasKeepEverythingSynced(long nativeProfileSyncServiceAndroid);
    private native boolean nativeHasUnrecoverableError(long nativeProfileSyncServiceAndroid);
    private native String nativeGetAboutInfoForTest(long nativeProfileSyncServiceAndroid);
    private native long nativeGetLastSyncedTimeForTest(long nativeProfileSyncServiceAndroid);
}

/* [<][>][^][v][top][bottom][index][help] */