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

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/signin/SigninHelper.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 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
OLDNEW
(Empty)
1 // Copyright 2015 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.signin;
6
7 import android.accounts.Account;
8 import android.content.Context;
9 import android.content.SharedPreferences;
10 import android.os.AsyncTask;
11 import android.preference.PreferenceManager;
12
13 import com.google.android.gms.auth.AccountChangeEvent;
14 import com.google.android.gms.auth.GoogleAuthException;
15 import com.google.android.gms.auth.GoogleAuthUtil;
16
17 import org.chromium.base.Log;
18 import org.chromium.base.VisibleForTesting;
19 import org.chromium.chrome.browser.invalidation.InvalidationController;
20 import org.chromium.chrome.browser.invalidation.InvalidationServiceFactory;
21 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
22 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
23 import org.chromium.chrome.browser.profiles.Profile;
24 import org.chromium.chrome.browser.signin.SigninManager.SignInFlowObserver;
25 import org.chromium.chrome.browser.sync.ProfileSyncService;
26 import org.chromium.chrome.browser.sync.SyncController;
27 import org.chromium.sync.AndroidSyncSettings;
28 import org.chromium.sync.internal_api.pub.base.ModelType;
29 import org.chromium.sync.signin.AccountManagerHelper;
30 import org.chromium.sync.signin.ChromeSigninController;
31
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Set;
37
38 /**
39 * A helper for tasks like re-signin.
40 *
41 * This should be merged into SigninManager when it is upstreamed.
42 */
43 public class SigninHelper {
44
45 private static final String TAG = "SigninHelper";
46
47 private static final Object LOCK = new Object();
48
49 private static final String ACCOUNTS_CHANGED_PREFS_KEY = "prefs_sync_account s_changed";
50
51 // Key to the shared pref that holds the new account's name if the currently signed
52 // in account has been renamed.
53 private static final String ACCOUNT_RENAMED_PREFS_KEY = "prefs_sync_account_ renamed";
54
55 // Key to the shared pref that holds the last read index of all the account changed
56 // events of the current signed in account.
57 private static final String ACCOUNT_RENAME_EVENT_INDEX_PREFS_KEY =
58 "prefs_sync_account_rename_event_index";
59
60 private static final String ANDROID_ACCOUNTS_PREFS_KEY = "prefs_sync_android _accounts";
61
62 private static SigninHelper sInstance;
63
64 /**
65 * Retrieve more detailed information from account changed intents.
66 */
67 public static interface AccountChangeEventChecker {
68 public List<String> getAccountChangeEvents(
69 Context context, int index, String accountName);
70 }
71
72 /**
73 * Uses GoogleAuthUtil.getAccountChangeEvents to detect if account
74 * renaming has occured.
75 */
76 public static final class SystemAccountChangeEventChecker
77 implements SigninHelper.AccountChangeEventChecker {
78 @Override
79 public List<String> getAccountChangeEvents(
80 Context context, int index, String accountName) {
81 try {
82 List<AccountChangeEvent> list = GoogleAuthUtil.getAccountChangeE vents(
83 context, index, accountName);
84 List<String> result = new ArrayList<String>(list.size());
85 for (AccountChangeEvent e : list) {
86 if (e.getChangeType() == GoogleAuthUtil.CHANGE_TYPE_ACCOUNT_ RENAMED_TO) {
87 result.add(e.getChangeData());
88 } else {
89 result.add(null);
90 }
91 }
92 return result;
93 } catch (IOException e) {
94 Log.w(TAG, "Failed to get change events", e);
95 } catch (GoogleAuthException e) {
96 Log.w(TAG, "Failed to get change events", e);
97 }
98 return new ArrayList<String>(0);
99 }
100 }
101
102
103 @VisibleForTesting
104 protected final Context mContext;
105
106 private final ChromeSigninController mChromeSigninController;
107
108 private final ProfileSyncService mProfileSyncService;
109
110 private final SigninManager mSigninManager;
111
112 private final OAuth2TokenService mOAuth2TokenService;
113
114 private final SyncController mSyncController;
115
116 public static SigninHelper get(Context context) {
117 synchronized (LOCK) {
118 if (sInstance == null) {
119 sInstance = new SigninHelper(context.getApplicationContext());
120 }
121 }
122 return sInstance;
123 }
124
125 private SigninHelper(Context context) {
126 mContext = context;
127 mProfileSyncService = ProfileSyncService.get(mContext);
128 mSigninManager = SigninManager.get(mContext);
129 mOAuth2TokenService = OAuth2TokenService.getForProfile(Profile.getLastUs edProfile());
130 mSyncController = SyncController.get(context);
131 mChromeSigninController = ChromeSigninController.get(mContext);
132 }
133
134 public void validateAccountSettings(boolean accountsChanged) {
135 Account syncAccount = mChromeSigninController.getSignedInUser();
136 if (syncAccount == null) {
137 if (SigninManager.getAndroidSigninPromoExperimentGroup() < 0) return ;
138
139 // Never shows a signin promo if user has manually disconnected.
140 String lastSyncAccountName =
141 PrefServiceBridge.getInstance().getSyncLastAccountName();
142 if (lastSyncAccountName != null && !lastSyncAccountName.isEmpty()) r eturn;
143
144 SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPr eferences(mContext);
145 boolean hasKnownAccountKeys = sharedPrefs.contains(ANDROID_ACCOUNTS_ PREFS_KEY);
146 // Nothing to do if Android accounts are not changed and already kno wn to Chrome.
147 if (hasKnownAccountKeys && !accountsChanged) return;
148
149 List<String> currentAccountNames =
150 AccountManagerHelper.get(mContext).getGoogleAccountNames();
151 if (hasKnownAccountKeys) {
152 ChromePreferenceManager chromePreferenceManager =
153 ChromePreferenceManager.getInstance(mContext);
154 if (!chromePreferenceManager.getSigninPromoShown()) {
155 Set<String> lastKnownAccountNames = sharedPrefs.getStringSet (
156 ANDROID_ACCOUNTS_PREFS_KEY, new HashSet<String>());
157 Set<String> newAccountNames = new HashSet<String>(currentAcc ountNames);
158 newAccountNames.removeAll(lastKnownAccountNames);
159 if (!newAccountNames.isEmpty()) {
160 chromePreferenceManager.setShowSigninPromo(true);
161 }
162 }
163 }
164
165 sharedPrefs.edit().putStringSet(
166 ANDROID_ACCOUNTS_PREFS_KEY, new HashSet<String>(currentAccou ntNames)).apply();
167 return;
168 }
169
170 String renamedAccount = getNewSignedInAccountName(mContext);
171 if (accountsChanged && renamedAccount != null) {
172 handleAccountRename(ChromeSigninController.get(mContext).getSignedIn AccountName(),
173 renamedAccount);
174 return;
175 }
176
177 // Always check for account deleted.
178 if (!accountExists(syncAccount)) {
179 // It is possible that Chrome got to this point without account
180 // rename notification. Let us signout before doing a rename.
181 // updateAccountRenameData(mContext, new SystemAccountChangeEventChe cker());
182 AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
183 @Override
184 protected Void doInBackground(Void... params) {
185 updateAccountRenameData(mContext, new SystemAccountChangeEve ntChecker());
186 return null;
187 }
188
189 @Override
190 protected void onPostExecute(Void result) {
191 String renamedAccount = getNewSignedInAccountName(mContext);
192 if (renamedAccount == null) {
193 mSigninManager.signOut(null, null);
194 } else {
195 validateAccountSettings(true);
196 }
197 }
198 };
199 task.execute();
200 return;
201 }
202
203 if (accountsChanged) {
204 // Account details have changed so inform the token service that cre dentials
205 // should now be available.
206 mOAuth2TokenService.validateAccounts(mContext, false);
207 }
208
209 if (AndroidSyncSettings.isSyncEnabled(mContext)) {
210 if (mProfileSyncService.hasSyncSetupCompleted()) {
211 if (accountsChanged) {
212 // Nudge the syncer to ensure it does a full sync.
213 InvalidationServiceFactory.getForProfile(Profile.getLastUsed Profile())
214 .requestSyncFromNativeChromeForAllTypes( );
215 }
216 } else {
217 // We should have set up sync but for some reason it's not enabl ed. Tell the sync
218 // engine to start.
219 mProfileSyncService.syncSignIn();
220 }
221 }
222 }
223
224 /**
225 * Deal with account rename. The current approach is to sign out and then si gn back in.
226 * In the (near) future, we should just be clearing all the cached email add ress here
227 * and have the UI re-fetch the emailing address based on the ID.
228 */
229 private void handleAccountRename(final String oldName, final String newName) {
230 Log.i(TAG, "handleAccountRename from: " + oldName + " to " + newName);
231
232 // TODO(acleung): I think most of the operations need to run on the main
233 // thread. May be we should have a progress Dialog?
234
235 // Before signing out, remember the current sync state and data types.
236 final boolean isSyncWanted = AndroidSyncSettings.isChromeSyncEnabled(mCo ntext);
237 final Set<ModelType> dataTypes = mProfileSyncService.getPreferredDataTyp es();
238
239 // TODO(acleung): Deal with passphrase or just prompt user to re-enter i t?
240
241 // Perform a sign-out with a callback to sign-in again.
242 mSigninManager.signOut(null, new Runnable() {
243 @Override
244 public void run() {
245 // Clear the shared perf only after signOut is successful.
246 // If Chrome dies, we can try it again on next run.
247 // Otherwise, if re-sign-in fails, we'll just leave chrome
248 // signed-out.
249 clearNewSignedInAccountName(mContext);
250 performResignin(newName, isSyncWanted, dataTypes);
251 }
252 });
253 }
254
255 private void performResignin(String newName,
256 final boolean isSyncWanted,
257 final Set<ModelType> dataTypes) {
258 // This is the correct account now.
259 final Account account = AccountManagerHelper.createAccountFromName(newNa me);
260
261 mSigninManager.startSignIn(null, account, true, new SignInFlowObserver() {
262 @Override
263 public void onSigninComplete() {
264 mProfileSyncService.setSetupInProgress(false);
265
266 if (isSyncWanted) {
267 mSyncController.start();
268 InvalidationController controller = InvalidationController.g et(mContext);
269 controller.refreshRegisteredTypes(dataTypes);
270 } else {
271 mSyncController.stop();
272 }
273
274 validateAccountSettings(true);
275 }
276
277 @Override
278 public void onSigninCancelled() {
279 }
280 });
281 }
282
283 private boolean accountExists(Account account) {
284 Account[] accounts = AccountManagerHelper.get(mContext).getGoogleAccount s();
285 for (Account a : accounts) {
286 if (a.equals(account)) {
287 return true;
288 }
289 }
290 return false;
291 }
292
293 /**
294 * Sets the ACCOUNTS_CHANGED_PREFS_KEY to true.
295 */
296 public static void markAccountsChangedPref(Context context) {
297 // The process may go away as soon as we return from onReceive but Andro id makes sure
298 // that in-flight disk writes from apply() complete before changing comp onent states.
299 PreferenceManager.getDefaultSharedPreferences(context)
300 .edit().putBoolean(ACCOUNTS_CHANGED_PREFS_KEY, true).apply();
301 }
302
303 /**
304 * @return The new account name of the current user. Null if it wasn't renam ed.
305 */
306 public static String getNewSignedInAccountName(Context context) {
307 return (PreferenceManager.getDefaultSharedPreferences(context)
308 .getString(ACCOUNT_RENAMED_PREFS_KEY, null));
309 }
310
311 private static void clearNewSignedInAccountName(Context context) {
312 PreferenceManager.getDefaultSharedPreferences(context)
313 .edit()
314 .putString(ACCOUNT_RENAMED_PREFS_KEY, null)
315 .apply();
316 }
317
318 private static String getLastKnownAccountName(Context context) {
319 // This is the last known name of the currently signed in user.
320 // It can be:
321 // 1. The signed in account name known to the ChromeSigninController.
322 // 2. A pending newly choosen name that is differed from the one known to
323 // ChromeSigninController but is stored in ACCOUNT_RENAMED_PREFS_KEY .
324 String name = PreferenceManager.getDefaultSharedPreferences(context).get String(
325 ACCOUNT_RENAMED_PREFS_KEY, null);
326
327 // If there is no pending rename, take the name known to ChromeSigninCon troller.
328 return name == null ? ChromeSigninController.get(context).getSignedInAcc ountName() : name;
329 }
330
331 public static void updateAccountRenameData(Context context) {
332 updateAccountRenameData(context, new SystemAccountChangeEventChecker());
333 }
334
335 @VisibleForTesting
336 public static void updateAccountRenameData(Context context, AccountChangeEve ntChecker checker) {
337 String curName = getLastKnownAccountName(context);
338
339 // Skip the search if there is no signed in account.
340 if (curName == null) return;
341
342 String newName = curName;
343
344 // This is the last read index of all the account change event.
345 int eventIndex = PreferenceManager.getDefaultSharedPreferences(context). getInt(
346 ACCOUNT_RENAME_EVENT_INDEX_PREFS_KEY, 0);
347
348 int newIndex = eventIndex;
349
350 try {
351 outerLoop: while (true) {
352 List<String> nameChanges = checker.getAccountChangeEvents(contex t,
353 newIndex, newName);
354
355 for (String name : nameChanges) {
356 if (name != null) {
357 // We have found a rename event of the current account.
358 // We need to check if that account is further renamed.
359 newName = name;
360 newIndex = 0; // Start from the beginning of the new acc ount.
361 continue outerLoop;
362 }
363 }
364
365 // If there is no rename event pending. Update the last read ind ex to avoid
366 // re-reading them in the future.
367 newIndex = nameChanges.size();
368 break;
369 }
370 } catch (Exception e) {
371 Log.w(TAG, "Error while looking for rename events.", e);
372 }
373
374 if (!curName.equals(newName)) {
375 PreferenceManager.getDefaultSharedPreferences(context)
376 .edit().putString(ACCOUNT_RENAMED_PREFS_KEY, newName).apply( );
377 }
378
379 if (newIndex != eventIndex) {
380 PreferenceManager.getDefaultSharedPreferences(context)
381 .edit().putInt(ACCOUNT_RENAME_EVENT_INDEX_PREFS_KEY, newInde x).apply();
382 }
383 }
384
385 public static boolean checkAndClearAccountsChangedPref(Context context) {
386 if (PreferenceManager.getDefaultSharedPreferences(context)
387 .getBoolean(ACCOUNTS_CHANGED_PREFS_KEY, false)) {
388 // Clear the value in prefs.
389 PreferenceManager.getDefaultSharedPreferences(context)
390 .edit().putBoolean(ACCOUNTS_CHANGED_PREFS_KEY, false).apply( );
391 return true;
392 } else {
393 return false;
394 }
395 }
396 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698