1 // Copyright 2014 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.shell.sync; 6 7 import android.accounts.Account; 8 import android.app.Activity; 9 import android.app.FragmentManager; 10 import android.content.Context; 11 import android.util.Log; 12 13 import org.chromium.base.ThreadUtils; 14 import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory; 15 import org.chromium.chrome.browser.identity.UuidBasedUniqueIdentificationGenerator; 16 import org.chromium.chrome.browser.invalidation.InvalidationController; 17 import org.chromium.chrome.browser.signin.SigninManager; 18 import org.chromium.chrome.browser.sync.ProfileSyncService; 19 import org.chromium.sync.notifier.SyncStatusHelper; 20 import org.chromium.sync.signin.AccountManagerHelper; 21 import org.chromium.sync.signin.ChromeSigninController; 22 23 /** 24 * A helper class for managing sync state for the ChromeShell. 25 * 26 * Builds on top of the ProfileSyncService (which manages Chrome's sync engine's state) and mimics 27 * the minimum additional functionality needed to fully enable sync for Chrome on Android. 28 */ 29 public class SyncController implements ProfileSyncService.SyncStateChangedListener, 30 SyncStatusHelper.SyncSettingsChangedObserver { 31 private static final String TAG = "SyncController"; 32 33 private static final String SESSIONS_UUID_PREF_KEY = "chromium.sync.sessions.id"; 34 35 private static SyncController sInstance; 36 37 private final Context mContext; 38 private final ChromeSigninController mChromeSigninController; 39 private final SyncStatusHelper mSyncStatusHelper; 40 private final ProfileSyncService mProfileSyncService; 41 SyncController(Context context)42 private SyncController(Context context) { 43 mContext = context; 44 mChromeSigninController = ChromeSigninController.get(mContext); 45 mSyncStatusHelper = SyncStatusHelper.get(context); 46 mSyncStatusHelper.registerSyncSettingsChangedObserver(this); 47 mProfileSyncService = ProfileSyncService.get(mContext); 48 mProfileSyncService.addSyncStateChangedListener(this); 49 50 setupSessionSyncId(); 51 mChromeSigninController.ensureGcmIsInitialized(); 52 } 53 54 /** 55 * Retrieve the singleton instance of this class. 56 * 57 * @param context the current context. 58 * @return the singleton instance. 59 */ get(Context context)60 public static SyncController get(Context context) { 61 ThreadUtils.assertOnUiThread(); 62 if (sInstance == null) { 63 sInstance = new SyncController(context.getApplicationContext()); 64 } 65 return sInstance; 66 } 67 68 /** 69 * Open a dialog that gives the user the option to sign in from a list of available accounts. 70 * 71 * @param fragmentManager the FragmentManager. 72 */ openSigninDialog(FragmentManager fragmentManager)73 public static void openSigninDialog(FragmentManager fragmentManager) { 74 AccountChooserFragment chooserFragment = new AccountChooserFragment(); 75 chooserFragment.show(fragmentManager, null); 76 } 77 78 /** 79 * Open a dialog that gives the user the option to sign out. 80 * 81 * @param fragmentManager the FragmentManager. 82 */ openSignOutDialog(FragmentManager fragmentManager)83 public static void openSignOutDialog(FragmentManager fragmentManager) { 84 SignoutFragment signoutFragment = new SignoutFragment(); 85 signoutFragment.show(fragmentManager, null); 86 } 87 88 /** 89 * Trigger Chromium sign in of the given account. 90 * 91 * This also ensure that sync setup is not in progress anymore, so sync will start after 92 * sync initialization has happened. 93 * 94 * @param activity the current activity. 95 * @param accountName the full account name. 96 */ signIn(Activity activity, String accountName)97 public void signIn(Activity activity, String accountName) { 98 final Account account = AccountManagerHelper.createAccountFromName(accountName); 99 100 // The SigninManager handles most of the sign-in flow, and doFinishSignIn handles the 101 // ChromeShell specific details. 102 SigninManager signinManager = SigninManager.get(mContext); 103 signinManager.onFirstRunCheckDone(); 104 final boolean passive = false; 105 signinManager.startSignIn(activity, account, passive, new SigninManager.Observer() { 106 @Override 107 public void onSigninComplete() { 108 SigninManager.get(mContext).logInSignedInUser(); 109 mProfileSyncService.setSetupInProgress(false); 110 mProfileSyncService.syncSignIn(); 111 start(); 112 } 113 114 @Override 115 public void onSigninCancelled() { 116 stop(); 117 } 118 }); 119 } 120 onStart()121 public void onStart() { 122 refreshSyncState(); 123 } 124 setupSessionSyncId()125 private void setupSessionSyncId() { 126 // Ensure that sync uses the correct UniqueIdentificationGenerator, but do not force the 127 // registration, in case a test case has already overridden it. 128 UuidBasedUniqueIdentificationGenerator generator = 129 new UuidBasedUniqueIdentificationGenerator(mContext, SESSIONS_UUID_PREF_KEY); 130 UniqueIdentificationGeneratorFactory.registerGenerator( 131 UuidBasedUniqueIdentificationGenerator.GENERATOR_ID, generator, false); 132 // Since we do not override the UniqueIdentificationGenerator, we get it from the factory, 133 // instead of using the instance we just created. 134 mProfileSyncService.setSessionsId(UniqueIdentificationGeneratorFactory 135 .getInstance(UuidBasedUniqueIdentificationGenerator.GENERATOR_ID)); 136 } 137 refreshSyncState()138 private void refreshSyncState() { 139 if (mSyncStatusHelper.isSyncEnabled()) 140 start(); 141 else 142 stop(); 143 } 144 start()145 private void start() { 146 ThreadUtils.assertOnUiThread(); 147 if (mSyncStatusHelper.isMasterSyncAutomaticallyEnabled()) { 148 Log.d(TAG, "Enabling sync"); 149 Account account = mChromeSigninController.getSignedInUser(); 150 InvalidationController.get(mContext).start(); 151 mProfileSyncService.enableSync(); 152 mSyncStatusHelper.enableAndroidSync(account); 153 } 154 } 155 stop()156 private void stop() { 157 ThreadUtils.assertOnUiThread(); 158 if (mChromeSigninController.isSignedIn()) { 159 Log.d(TAG, "Disabling sync"); 160 Account account = mChromeSigninController.getSignedInUser(); 161 InvalidationController.get(mContext).stop(); 162 mProfileSyncService.disableSync(); 163 mSyncStatusHelper.disableAndroidSync(account); 164 } 165 } 166 167 /** 168 * From {@link ProfileSyncService.SyncStateChangedListener}. 169 */ 170 @Override syncStateChanged()171 public void syncStateChanged() { 172 ThreadUtils.assertOnUiThread(); 173 // If sync has been disabled from the dashboard, we must disable it. 174 Account account = mChromeSigninController.getSignedInUser(); 175 boolean isSyncSuppressStart = mProfileSyncService.isStartSuppressed(); 176 boolean isSyncEnabled = mSyncStatusHelper.isSyncEnabled(account); 177 if (account != null && isSyncSuppressStart && isSyncEnabled) 178 stop(); 179 } 180 181 /** 182 * From {@link SyncStatusHelper.SyncSettingsChangedObserver}. 183 */ 184 @Override syncSettingsChanged()185 public void syncSettingsChanged() { 186 ThreadUtils.runOnUiThread(new Runnable() { 187 @Override 188 public void run() { 189 refreshSyncState(); 190 } 191 }); 192 } 193 } 194